[
  {
    "path": ".c8rc.json",
    "content": "{\n  \"all\": true,\n  \"reporter\": [\n    \"lcov\",\n    \"text\",\n    \"html\",\n    \"text-summary\"\n  ],\n  \"include\": [\n    \"lib/**/*.js\",\n    \"index.js\"\n  ]\n}\n"
  },
  {
    "path": ".dockerignore",
    "content": "# Ignore everything but the stuff following the `*` with the `!`\n# See https://docs.docker.com/engine/reference/builder/#dockerignore-file\n\n*\n!package.json\n!lib\n!deps\n!build\n"
  },
  {
    "path": ".editorconfig",
    "content": "# https://editorconfig.org/\n\nroot = true\n\n[*]\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug Report\nabout: Report an issue\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n## Bug Description\n\n<!-- A clear and concise description of what the bug is. -->\n\n## Reproducible By\n\n<!-- A step by step list on how the bug can be reproduced for examination. -->\n\n## Expected Behavior\n\n<!-- A clear and concise description of what you expected to happen. -->\n\n## Logs & Screenshots\n\n<!-- If applicable, add screenshots to help explain your problem, or \nalternatively add your console logs here. -->\n\n## Environment\n\n<!-- This is just your OS and environment information [e.g. Ubuntu 18.04 LTS,\nNode v14.14.0] -->\n\n### Additional context\n\n<!-- Add any other context about the problem here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature Request\nabout: Make a suggestion on a feature or improvement for the project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n## This would solve...\n\n<!-- A clear and concise description of the problem this feature request relates\nto, if applicable. -->\n\n## The implementation should look like...\n\n<!-- A clear and concise description of how you expect this to be resolved or\nimplemented. -->\n\n## I have also considered...\n\n<!-- A clear and concise description of any alternative solutions or features\nyou have considered. -->\n\n## Additional context\n\n<!-- Add any other context, screenshots or ideas about the feature request\nhere. -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nBefore submitting a Pull Request, please read our contribution guidelines, which\ncan be found at CONTRIBUTING.md in the repository root.\n\nFor code changes:\n1. Include tests for any bug fixes or new features.\n2. Update documentation if relevant.\n3. Ensure that tests and linting pass.\n\nYou will also need to ensure that your contribution complies with the\nDeveloper's Certificate of Origin, outlined in CONTRIBUTING.md\n-->\n\n## This relates to...\n\n<!-- List the issues this resolves or relates to here (if applicable) -->\n\n## Rationale\n\n<!-- Briefly explain the purpose of this pull request, if not already\njustifiable with the above section. If it is, you may omit this section. -->\n\n## Changes\n\n<!-- Write a summary or list of changes here -->\n\n### Features\n\n<!-- List the new features here (if applicable), or write N/A if not -->\n\n### Bug Fixes\n\n<!-- List the fixed bugs here (if applicable), or write N/A if not -->\n\n### Breaking Changes and Deprecations\n\n<!-- List the breaking changes (changes that modify the existing API) and\ndeprecations (removed features) here -->\n\n## Status\n\n<!-- KEY: S = Skipped, x = complete -->\n\n\n- [ ] I have read and agreed to the [Developer's Certificate of Origin][cert]\n- [ ] Tested\n- [ ] Benchmarked (**optional**)\n- [ ] Documented\n- [ ] Review ready\n- [ ] In review\n- [ ] Merge ready\n\n[cert]: https://github.com/nodejs/undici/blob/main/CONTRIBUTING.md#developers-certificate-of-origin\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: \"npm\"\n    directory: /docs\n    schedule:\n      interval: \"weekly\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: \"npm\"\n    directory: /benchmarks\n    schedule:\n      interval: \"weekly\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: docker\n    directory: /build\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/workflows/autobahn.yml",
    "content": "name: Autobahn\n\non:\n  workflow_dispatch:\n  workflow_call:\n    inputs:\n      node-version:\n        default: '24'\n        type: string\n  pull_request:\n    paths:\n      - '.github/workflows/autobahn.yml'\n      - 'lib/web/websocket/**'\n      - 'test/autobahn/**'\n\npermissions:\n  contents: read\n\njobs:\n  autobahn:\n    name: Autobahn Test Suite\n    runs-on: ubuntu-latest\n    container: node:24\n    services:\n      fuzzingserver:\n        image: crossbario/autobahn-testsuite:latest\n        ports:\n          - '9001:9001'\n        options: --name fuzzingserver\n        volumes:\n          - ${{ github.workspace }}/test/autobahn/config:/config\n          - ${{ github.workspace }}/test/autobahn/reports:/reports\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          clean: false\n      \n      - name: Restart Autobahn Server\n        # Restart service after volumes have been checked out\n        uses: docker://docker\n        with:\n          args: docker restart --time 0 --signal=SIGKILL fuzzingserver\n\n      - name: Setup Node.js@${{ inputs.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ inputs.node-version }}\n\n      - name: Run Autobahn Test Suite\n        run: npm run test:websocket:autobahn\n        env:\n          FUZZING_SERVER_URL: ws://fuzzingserver:9001\n          LOG_ON_ERROR: false\n\n      - name: Report CI\n        id: report-ci\n        run: npm run test:websocket:autobahn:report\n        env:\n          FAIL_ON_ERROR: true\n"
  },
  {
    "path": ".github/workflows/backport.yml",
    "content": "name: Backport\non:\n  pull_request_target:\n    types:\n      - closed\n      - labeled\n\njobs:\n  backport:\n    name: Backport\n    runs-on: ubuntu-latest\n    if: >\n      github.event.pull_request.merged\n      && (\n        github.event.action == 'closed'\n        || (\n          github.event.action == 'labeled'\n          && contains(github.event.label.name, 'backport')\n        )\n      )\n    permissions:\n      pull-requests: write\n      contents: write\n    steps:\n      - name: Backport\n        uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2.0.4\n        id: backport\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/bench.yml",
    "content": "name: Benchmarks\n\non:\n  push:\n    branches:\n     - main\n     - current\n     - next\n     - 'v*'\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  benchmark_current:\n    name: benchmark current\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          ref: ${{ github.base_ref }}\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench\n        working-directory: ./benchmarks\n\n  benchmark_branch:\n    name: benchmark branch\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench\n        working-directory: ./benchmarks\n\n  benchmark_post_current:\n    name: benchmark (sending data) current\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          ref: ${{ github.base_ref }}\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench-post\n        working-directory: ./benchmarks\n\n  benchmark_post_branch:\n    name: benchmark (sending data) branch\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench-post\n        working-directory: ./benchmarks\n\n  benchmark_current_h2:\n    name: benchmark current h2\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          ref: ${{ github.base_ref }}\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench:h2\n        working-directory: ./benchmarks\n\n  benchmark_branch_h2:\n    name: benchmark branch h2\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - name: Setup Node\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n      - name: Install Modules for undici\n        run: npm i --ignore-scripts --omit=dev\n      - name: Install Modules for Benchmarks\n        run: npm i\n        working-directory: ./benchmarks\n      - name: Run Benchmark\n        run: npm run bench:h2\n        working-directory: ./benchmarks\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n     - main\n     - current\n     - next\n     - 'v*'\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  dependency-review:\n    if: ${{ github.event_name == 'pull_request' }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Harden Runner\n        uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1\n        with:\n          egress-policy: audit\n\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Dependency Review\n        uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          # Using `latest` as `lts` could point to previous major version.\n          # Different versions of Node.js can cause different linting results\n          # (e.g. dependening on process.getBuiltinModule())\n          node-version: 'latest'\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Lint\n        run: npm run lint\n\n  test:\n    name: Test ${{ matrix.node-version == '24' && matrix.runs-on == 'ubuntu-latest' && 'and Coverage ' || ''}}with Node.js ${{ matrix.node-version }} on ${{ matrix.runs-on }}\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['20', '22', '24', '25']\n        runs-on: ['ubuntu-latest', 'windows-latest', 'macos-latest']\n        exclude:\n          - node-version: '20'\n            runs-on: windows-latest\n    uses: ./.github/workflows/nodejs.yml\n    with:\n      # Disable coverage on Node.js 25 until https://github.com/nodejs/node/issues/61971 is resolved.\n      codecov: ${{ matrix.node-version == '24' && matrix.runs-on == 'ubuntu-latest' }}\n      node-version: ${{ matrix.node-version }}\n      runs-on: ${{ matrix.runs-on }}\n    secrets: inherit\n\n  test-with-no-wasm-simd:\n    name: Test with Node.js ${{ matrix.node-version }} on ${{ matrix.runs-on }} with WASM SIMD disabled\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['24', '25']\n        runs-on: ['ubuntu-latest']\n    uses: ./.github/workflows/nodejs.yml\n    with:\n      node-version: ${{ matrix.node-version }}\n      runs-on: ${{ matrix.runs-on }}\n      no-wasm-simd: '1'\n    secrets: inherit\n\n  test-without-intl:\n    name: Test with Node.js ${{ matrix.node-version }} compiled --without-intl\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['20', '22', '24', '25']\n    runs-on: ubuntu-latest\n    timeout-minutes: 120\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          submodules: recursive\n\n      # Setup node, install deps, and build undici prior to building icu-less node and testing\n      - name: Setup Node.js@${{ matrix.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Build undici\n        run: npm run build:node\n\n      - name: Determine latest release\n        id: release\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        with:\n          result-encoding: string\n          script: |\n            const req = await fetch('https://nodejs.org/download/release/index.json')\n            const releases = await req.json()\n\n            const latest = releases.find((r) => r.version.startsWith('v${{ matrix.node-version }}'))\n            return latest.version\n\n      - name: Download and extract source\n        run: curl https://nodejs.org/download/release/${{ steps.release.outputs.result }}/node-${{ steps.release.outputs.result }}.tar.xz | tar xfJ -\n\n      - name: Install ninja\n        run: sudo apt-get install ninja-build\n\n      - name: ccache\n        uses: hendrikmuhs/ccache-action@bfa03e1de4d7f7c3e80ad9109feedd05c4f5a716 #v1.2.19\n        with:\n          key: node${{ matrix.node-version }}\n\n      - name: Build node\n        working-directory: ./node-${{ steps.release.outputs.result }}\n        run: |\n          export CC=\"ccache gcc\"\n          export CXX=\"ccache g++\"\n          ./configure --without-intl --ninja --prefix=./final\n          make\n          make install\n          echo \"$(pwd)/final/bin\" >> $GITHUB_PATH\n\n      - name: Print version information\n        run: |\n          echo OS: $(node -p \"os.version()\")\n          echo Node.js: $(node --version)\n          echo \"Node.js built-in dependencies: $(node -p \"'\\r\\n' + (Object.entries(process.versions).map(([k, v], i, arr) => (i !== arr.length - 1 ? '├──' : '└──') + k + '@' + v)).join('\\r\\n')\")\"\n          echo npm: $(npm --version)\n          echo git: $(git --version)\n          echo icu config: $(node -e \"console.log(process.config)\" | grep icu)\n\n      - name: Configure hosts file for WPT (Windows)\n        if: runner.os == 'Windows'\n        run: |\n          cd ${{ github.workspace }}\\test\\web-platform-tests\\wpt\n          python wpt make-hosts-file | Out-File $env:SystemRoot\\System32\\drivers\\etc\\hosts -Encoding ascii -Append\n        shell: powershell\n\n      - name: Configure hosts file for WPT (Unix)\n        if: runner.os != 'Windows'\n        run: |\n          cd ${{ github.workspace }}/test/web-platform-tests/wpt\n          python3 wpt make-hosts-file | sudo tee -a /etc/hosts\n\n      - name: Run tests\n        run: npm run test:javascript:without-intl\n\n  test-without-ssl:\n    name: Test with Node.js ${{ matrix.node-version }} compiled --without-ssl\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['20', '22', '24', '25']\n    runs-on: ubuntu-latest\n    timeout-minutes: 120\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          submodules: recursive\n\n      # Setup node, install deps, and build undici prior to building icu-less node and testing\n      - name: Setup Node.js@${{ matrix.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Build undici\n        run: npm run build:node\n\n      - name: Determine latest release\n        id: release\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        with:\n          result-encoding: string\n          script: |\n            const req = await fetch('https://nodejs.org/download/release/index.json')\n            const releases = await req.json()\n\n            const latest = releases.find((r) => r.version.startsWith('v${{ matrix.node-version }}'))\n            return latest.version\n\n      - name: Download and extract source\n        run: curl https://nodejs.org/download/release/${{ steps.release.outputs.result }}/node-${{ steps.release.outputs.result }}.tar.xz | tar xfJ -\n\n      - name: Install ninja\n        run: sudo apt-get install ninja-build\n\n      - name: ccache\n        uses: hendrikmuhs/ccache-action@bfa03e1de4d7f7c3e80ad9109feedd05c4f5a716 #v1.2.19\n        with:\n          key: node${{ matrix.node-version }}\n\n      - name: Build node\n        working-directory: ./node-${{ steps.release.outputs.result }}\n        run: |\n          export CC=\"ccache gcc\"\n          export CXX=\"ccache g++\"\n          ./configure --without-ssl --ninja --prefix=./final\n          make\n          make install\n          echo \"$(pwd)/final/bin\" >> $GITHUB_PATH\n\n      - name: Print version information\n        run: |\n          echo OS: $(node -p \"os.version()\")\n          echo Node.js: $(node --version)\n          echo \"Node.js built-in dependencies: $(node -p \"'\\r\\n' + (Object.entries(process.versions).map(([k, v], i, arr) => (i !== arr.length - 1 ? '├──' : '└──') + k + '@' + v)).join('\\r\\n')\")\"\n          echo npm: $(npm --version)\n          echo git: $(git --version)\n          echo icu config: $(node -e \"console.log(process.config)\" | grep icu)\n\n      - name: Try loading Node.js without Crypto\n        run: node index.js\n\n  test-fuzzing:\n    name: Fuzzing\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: lts/*\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Run fuzzing tests\n        run: npm run test:fuzzing\n\n  test-shared-builtin:\n    name: Test with Node.js ${{ matrix.node-version }} compiled --shared-builtin-undici/undici-path\n    uses: ./.github/workflows/nodejs-shared.yml\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['24', '25']\n        runs-on: ['ubuntu-latest']\n    with:\n      node-version: ${{ matrix.node-version }}\n\n  test-types:\n    name: Test TypeScript types\n    timeout-minutes: 15\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: lts/*\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Run typings tests\n        run: npm run test:typescript\n\n  automerge:\n    if: >\n      github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'\n    needs:\n      - dependency-review\n      - test\n      - test-types\n      - test-with-no-wasm-simd\n      - test-without-intl\n      - test-fuzzing\n      - lint\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n    steps:\n      - name: Merge Dependabot PR\n        uses: fastify/github-action-merge-dependabot@1b2ed42db8f9d81a46bac83adedfc03eb5149dff # v3.11.2\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [\"main\"]\n  schedule:\n    - cron: \"0 0 * * 1\"\n\npermissions:\n  contents: read\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"javascript\", \"typescript\"]\n        # CodeQL supports [ $supported-codeql-languages ]\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    steps:\n      - name: Harden Runner\n        uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1\n        with:\n          egress-policy: audit\n\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v2.3.3\n        with:\n          languages: ${{ matrix.language }}\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@9e907b5e64f6b83e7804b09294d44122997950d6 # v2.3.3\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n      #   If the Autobuild fails above, remove it and uncomment the following three lines.\n      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n      # - run: |\n      #   echo \"Run, Build Application using script\"\n      #   ./location_of_script_within_repo/buildscript.sh\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v2.3.3\n        with:\n          category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/nodejs-nightly.yml",
    "content": "name: Node.js Nightly\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 10 * * *\"\n\npermissions:\n  contents: read\n\njobs:\n  test:\n    name: Test with Node.js ${{ matrix.node-version }} on ${{ matrix.runs-on }}\n    if: github.repository == 'nodejs/undici'\n    strategy:\n      fail-fast: false\n      max-parallel: 0\n      matrix:\n        node-version: ['25-nightly']\n        runs-on: [ubuntu-latest, windows-latest, macos-latest]\n    uses: ./.github/workflows/nodejs.yml\n    with:\n      node-version: ${{ matrix.node-version }}\n      runs-on: ${{ matrix.runs-on }}\n    secrets: inherit\n\n  autobahn:\n    if: github.repository == 'nodejs/undici'\n    uses: ./.github/workflows/autobahn.yml\n    with:\n      node-version: '25-nightly'\n    secrets: inherit\n\n  test-shared-builtin:\n    if: github.repository == 'nodejs/undici'\n    uses: ./.github/workflows/nodejs-shared.yml\n    with:\n      node-version: '25'\n      node-download-server-path: '/nightly'\n\n  report-failure:\n    if: ${{ always() && (needs.test.result == 'failure' || needs.test-shared-builtin.result == 'failure' || needs.autobahn.result == 'failure') }}\n    needs:\n      - test\n      - test-shared-builtin\n      - autobahn\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - name: Create or update issue\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        with:\n          script: |\n            const ISSUE_TITLE = \"Nightly tests are failing\"\n\n            const actionRunUrl = \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"\n\n            const issueContext = {\n              owner: context.repo.owner,\n              repo: context.repo.repo\n            }\n\n            let issue = (await github.rest.issues.listForRepo({\n              state: \"open\",\n              creator: \"github-actions[bot]\",\n              ...issueContext\n            })).data.find((issue) => issue.title === ISSUE_TITLE)\n\n            if(!issue) {\n              issue = (await github.rest.issues.create({\n                title: ISSUE_TITLE,\n                body: `Tests against nightly failed, see: ${actionRunUrl}`,\n                ...issueContext\n              })).data\n            }\n"
  },
  {
    "path": ".github/workflows/nodejs-shared.yml",
    "content": "name: Node.js compiled --shared-builtin-undici/undici-path CI\n\non:\n  workflow_call:\n    inputs:\n      node-version:\n        required: true\n        type: string\n      node-download-server-path:\n        required: false\n        type: string\n        default: '/release'\n\npermissions:\n  contents: read\n\njobs:\n  test-shared-builtin:\n    name: Test with Node.js ${{ inputs.node-version }} compiled --shared-builtin-undici/undici-path\n    runs-on: ubuntu-latest\n    timeout-minutes: 120\n    steps:\n      # Checkout into a subdirectory otherwise Node.js tests will break due to finding Undici's package.json in a parent directory.\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          path: ./undici\n          persist-credentials: false\n\n      # Setup node, install deps, and build undici prior to building node with `--shared-builtin-undici/undici-path` and testing\n      - name: Setup Node.js lts/*\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n\n      - name: Install dependencies\n        working-directory: ./undici\n        run: npm install\n\n      - name: Install wasi-libc\n        run: sudo apt-get install -y wasi-libc binaryen\n\n      - name: Build WASM\n        working-directory: ./undici\n        run: |\n          export EXTERNAL_PATH=${{ github.workspace }}/undici\n          export WASM_CC=clang\n          export WASM_CFLAGS='--target=wasm32-wasi --sysroot=/usr'\n          export WASM_LDFLAGS='-nodefaultlibs'\n          export WASM_LDLIBS='-lc'\n          node build/wasm.js\n\n      - name: Determine latest release for Node.js ${{ inputs.node-version }}\n        id: release\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        with:\n          result-encoding: string\n          script: |\n            const req = await fetch('https://nodejs.org/download${{ inputs.node-download-server-path }}/index.json')\n            const releases = await req.json()\n\n            const latest = releases.find((r) => r.version.startsWith('v${{ inputs.node-version }}'))\n            return latest.version\n\n      - name: Download and extract source for Node.js ${{ steps.release.outputs.result }}\n        run: curl https://nodejs.org/download${{ inputs.node-download-server-path }}/${{ steps.release.outputs.result }}/node-${{ steps.release.outputs.result }}.tar.xz | tar xfJ -\n\n      - name: Install ninja\n        run: sudo apt-get install ninja-build\n\n      - name: ccache\n        uses: hendrikmuhs/ccache-action@bfa03e1de4d7f7c3e80ad9109feedd05c4f5a716 #v1.2.19\n        with:\n          key: node(external_undici)${{ inputs.node-version }}\n\n      - name: Build node ${{ steps.release.outputs.result }} with --shared-builtin-undici/undici-path\n        working-directory: ./node-${{ steps.release.outputs.result }}\n        run: |\n          export CC=\"ccache gcc\"\n          export CXX=\"ccache g++\"\n          rm -rf deps/undici\n          ./configure --shared-builtin-undici/undici-path ${{ github.workspace }}/undici/loader.js --ninja --prefix=./final\n          make\n          make install\n          echo \"$(pwd)/final/bin\" >> $GITHUB_PATH\n\n      - name: Print version information\n        run: |\n          echo OS: $(node -p \"os.version()\")\n          echo Node.js: $(node --version)\n          echo \"Node.js built-in dependencies: $(node -p \"'\\r\\n' + (Object.entries(process.versions).map(([k, v], i, arr) => (i !== arr.length - 1 ? '├──' : '└──') + k + '@' + v)).join('\\r\\n')\")\"\n          echo npm: $(npm --version)\n          echo git: $(git --version)\n          echo external config: $(node -e \"console.log(process.config)\" | grep NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH)\n          echo Node.js built-in undici version: $(node -p \"process.versions.undici\") # undefined for external Undici\n\n      - name: Run tests\n        working-directory: ./node-${{ steps.release.outputs.result }}\n        run: tools/test.py -p dots --flaky-tests=dontcare\n\n"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "content": "name: Node.js\n\non:\n  workflow_call:\n    inputs:\n      node-version:\n        required: true\n        type: string\n      runs-on:\n        required: true\n        type: string\n      codecov:\n        required: false\n        type: boolean\n        default: false\n      no-wasm-simd:\n        type: string\n        required: false\n        default: ''\n\npermissions:\n  contents: read\n\njobs:\n  test:\n    name: Test ${{ inputs.codecov == true && 'and Coverage ' || '' }}with Node.js ${{ inputs.node-version }} on ${{ inputs.runs-on }}\n    timeout-minutes: 20\n    runs-on: ${{ inputs.runs-on }}\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n          submodules: recursive\n\n      - name: Setup Node.js@${{ inputs.node-version }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ inputs.node-version }}\n\n      - name: Print version information\n        run: |\n          echo OS: $(node -p \"os.version()\")\n          echo Node.js: $(node --version)\n          echo \"Node.js built-in dependencies: $(node -p \"'\\r\\n' + (Object.entries(process.versions).map(([k, v], i, arr) => (i !== arr.length - 1 ? '├──' : '└──') + k + '@' + v)).join('\\r\\n')\")\"\n          echo npm: $(npm --version)\n          echo git: $(git --version)\n\n      - name: Install dependencies\n        run: npm install\n\n      - name: Print installed dependencies\n        run: npm ls --all\n        continue-on-error: true\n\n      - name: Configure hosts file for WPT (Windows)\n        if: runner.os == 'Windows'\n        run: |\n          cd ${{ github.workspace }}\\test\\web-platform-tests\\wpt\n          python wpt make-hosts-file | Out-File $env:SystemRoot\\System32\\drivers\\etc\\hosts -Encoding ascii -Append\n        shell: powershell\n\n      - name: Configure hosts file for WPT (Unix)\n        if: runner.os != 'Windows'\n        run: |\n          cd ${{ github.workspace }}/test/web-platform-tests/wpt\n          python3 wpt make-hosts-file | sudo tee -a /etc/hosts\n\n      - name: Generate PEM files\n        run: npm run generate-pem\n        id: generate-pem\n      \n      - name: Test unit\n        run: npm run test:unit\n        id: test-unit\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test node-test\n        run: npm run test:node-test\n        id: test-node-test\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test fetch\n        run: npm run test:fetch\n        id: test-fetch\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test node-fetch\n        run: npm run test:node-fetch\n        id: test-node-fetch\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test cache\n        run: npm run test:cache\n        id: test-cache\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test cache-interceptor  ${{ inputs.node-version != '20' && 'with' || 'without' }} sqlite\n        run: npm run test:cache-interceptor\n        id: test-cache-interceptor\n        env:\n          CI: true\n          NODE_OPTIONS: ${{ inputs.node-version != '20' && '--experimental-sqlite' || '' }}\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n\n      - name: Test cache-tests\n        run: npm run test:cache-tests\n        id: test-cache-tests\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test cookies\n        run: npm run test:cookies\n        id: test-cookies\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n\n      - name: Test interceptors\n        run: npm run test:interceptors\n        id: test-interceptors\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test eventsource\n        run: npm run test:eventsource\n        id: test-eventsource\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test infra\n        run: npm run test:infra\n        id: test-infra\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n\n      - name: Test subresource-integrity\n        run: npm run test:subresource-integrity\n        id: test-subresource-integrity\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test websocket\n        run: npm run test:websocket\n        id: test-websocket\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test jest\n        run: npm run test:jest\n        id: test-jest\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ''\n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n      \n      - name: Test wpt\n        run: npm run test:wpt\n        id: test-wpt\n        env:\n          CI: true\n          NODE_V8_COVERAGE: ${{ inputs.codecov == true && './coverage/tmp' || '' }} \n          UNDICI_NO_WASM_SIMD: ${{ inputs['no-wasm-simd'] }}\n\n      - name: Coverage summary\n        if: inputs.codecov == true\n        run: npm run coverage:report:ci\n        id: prepare-coverage-report\n      \n      - name: Upload coverage report to Codecov\n        if: inputs.codecov == true\n        uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/release-create-pr.yml",
    "content": "name: Create release PR\n\npermissions:\n  contents: read\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'The version number to release (has priority over release_type)'\n        type: string\n      release_type:\n        description: Type of release\n        type: choice\n        default: patch\n        options:\n          - patch\n          - minor\n          - major\n\njobs:\n  create-pr:\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: write\n      pull-requests: write\n\n    outputs:\n      version: ${{ steps.bump.outputs.version }}\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        id: checkout\n        with:\n          persist-credentials: true\n\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        id: setup-node\n        with:\n          node-version: 'lts/*'\n      - name: Git Config\n        id: git-config\n        run: |\n          git config --global user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"github-actions[bot]\"\n\n      - name: Change version number and push\n        id: bump\n        run: |\n          npm version ${{ inputs.version || inputs.release_type }} --git-tag-version=false\n          VERSION=`jq -r \".version\" package.json`\n          RELEASE_BRANCH=\"release/v$VERSION\"\n          git add -u\n          git commit -m \"Bumped v$VERSION\"\n          git push origin \"HEAD:$RELEASE_BRANCH\"\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n\n      - name: Create PR\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        id: create-pr\n        with:\n          script: |\n            const defaultBranch = \"${{ github.event.repository.default_branch }}\"\n            const versionTag = \"v${{ steps.bump.outputs.version }}\"\n            const commitHash = \"${{ github.sha }}\"\n            await require('./scripts/release').generatePr({ github, context, defaultBranch, versionTag, commitHash })\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release undici and undici-types on NPM and create GitHub Release\n\non:\n  push:\n    branches:\n     - main\n    paths:\n      - package.json\n\npermissions:\n  contents: read\n\njobs:\n  determine-release-version:\n    runs-on: ubuntu-latest\n    outputs:\n      release-version: ${{ steps.determine-release-version.outputs.result }}\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        id: checkout\n        with:\n          persist-credentials: false\n      - name: Determine release version\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        id: determine-release-version\n        with:\n          result-encoding: string\n          script: |\n            const { owner, repo } = context.repo\n            const version = require(\"./package.json\").version\n            const versionTag = `v${version}`\n\n            const { data: releases } = await github.rest.repos.listReleases({\n              owner,\n              repo\n            })\n\n            const previousRelease = releases.find((r) => r.tag_name.startsWith('v7'))\n\n            if (versionTag !== previousRelease?.tag_name) {\n              return versionTag\n            }\n\n  release:\n    runs-on: ubuntu-latest\n    needs: determine-release-version\n    if: ${{ startsWith(needs.determine-release-version.outputs.release-version, 'v') }}\n\n    permissions:\n      contents: write\n      id-token: write\n\n    environment: release\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        id: checkout\n        with:\n          persist-credentials: true\n\n      - name: Setup Node.js\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Install globally latest npm\n        run: npm install -g npm@latest\n        id: install-globally-latest-npm\n\n      - name: Install dependencies\n        run: npm install\n        id: install-dependencies\n\n      - name: Publish undici on NPM\n        run: npm publish --access public\n        id: npm-publish-undici\n\n      - name: Generate Types Package\n        run: node scripts/generate-undici-types-package-json.js\n        id: generate-types-package\n\n      - name: Publish undici-types on NPM\n        run: npm publish\n        id: npm-publish-undici-types\n        working-directory: './types'\n\n      - name: Create GitHub release\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0\n        id: create-gh-release\n        with:\n          script: |\n            const defaultBranch = \"${{ github.event.repository.default_branch }}\"\n            const versionTag = \"${{ needs.determine-release-version.outputs.release-version }}\"\n            const commitHash = \"${{ github.sha }}\"\n            await require('./scripts/release').release({ github, context, defaultBranch, versionTag, commitHash })\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: '16 10 * * 2'\n  push:\n    branches: [ \"main\" ]\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard.\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@9e907b5e64f6b83e7804b09294d44122997950d6 # v3.29.5\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".github/workflows/triggered-autobahn.yml",
    "content": "name: Autobahn\n\non:\n  pull_request:\n    types:\n      - labeled\n  \njobs:\n  autobahn:\n    if: ${{ github.event.label.name == 'autobahn' }}\n    name: Autobahn Test Suite\n    uses: ./.github/workflows/autobahn.yml\n"
  },
  {
    "path": ".github/workflows/update-submodules.yml",
    "content": "name: Update Submodules\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 * * *'\n\njobs:\n  update-wpt:\n    name: Update Submodules\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n    steps:\n      - name: Checkout Repository\n        id: checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: true\n  \n      - name: Git Config\n        id: git-config\n        run: |\n          git config --global user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"github-actions[bot]\"\n\n      - name: Update Submodules\n        id: update-submodules\n        run: |\n          git submodule update --init --recursive --remote --merge\n\n      - name: Check for Changes\n        id: check-for-changes\n        run: |\n          if git diff --quiet; then\n            echo \"no changes detected\"\n            echo \"change=false\" >> \"$GITHUB_OUTPUT\"\n          else\n            echo \"changes detected\"\n            echo \"change=true\" >> \"$GITHUB_OUTPUT\"\n          fi\n      \n      - name: Create Branch\n        id: create-branch\n        if: ${{ steps.check-for-changes.outputs.change == 'true'}}\n        run: |\n          git checkout -b submodules-update\n          git add .\n          git commit -m \"chore: update Submodules\"\n          git push --force --set-upstream origin submodules-update\n      \n      - name: Create Pull Request\n        id: create-pr\n        if: ${{ steps.check-for-changes.outputs.change == 'true'}}\n        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0\n        with:\n          base: main\n          branch: submodules-update\n          title: Update Submodules\n          body: Automated update of the Submodules\n          commit-message: \"chore: update Submodules\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n# lock files\npackage-lock.json\nyarn.lock\npnpm-lock.yaml\n\n# IDE files\n.idea\n.vscode\n\n*0x\n*clinic*\n\n# Fuzzing\ncorpus/\ncrash-*\nfuzz-results-*.json\n\n# Bundle output\nundici-fetch.js\n/test/imports/undici-import.js\n\n# .npmrc has platform specific value for windows\n.npmrc\n\n.tap\n\n# File generated by /test/request-timeout.js\ntest/request-timeout.10mb.bin\n\n# Claude files\nCLAUDE.md\n.claude\n\n# Ignore .pi\n.pi\n\n# Ignore .githuman\n.githuman\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"test/web-platform-tests/wpt\"]\n\tpath = test/web-platform-tests/wpt\n\turl = https://github.com/web-platform-tests/wpt.git\n[submodule \"test/fixtures/cache-tests\"]\n\tpath = test/fixtures/cache-tests\n\turl = https://github.com/http-tests/cache-tests\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npm run lint\n"
  },
  {
    "path": ".npmignore",
    "content": "*\n!lib/**/*\n!index.js\n!index-fetch.js\n\n# The wasm files are stored as base64 strings in the corresponding .js files\nlib/llhttp/llhttp_simd.wasm\nlib/llhttp/llhttp.wasm\n\n!types/**/*\n!index.d.ts\n!docs/docs/**/*\n!scripts/strip-comments.js\n\n# File generated by /test/request-timeout.js\ntest/request-timeout.10mb.bin\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nUndici is committed to upholding the Node.js Code of Conduct.\n\nThe Node.js Code of Conduct document can be found at\nhttps://github.com/nodejs/admin/blob/main/CODE_OF_CONDUCT.md\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Undici\n\n* [Guides](#guides)\n  * [Update `llhttp`](#update-llhttp)\n  * [Lint](#lint)\n  * [Test](#test)\n  * [Coverage](#coverage)\n  * [Releases](#releases)\n  * [Update `WPTs`](#update-wpts)\n  * [Building for externally shared node builtins](#external-builds)\n  *  [Benchmarks](#benchmarks)\n  *  [Documentation](#documentation)\n* [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin)\n  * [Moderation Policy](#moderation-policy)\n\n<a id=\"guides\"></a>\n## Guides\n\nThis is a collection of guides on how to run and update `undici`, and how to run different parts of the project.\n\n<a id=\"update-llhttp\"></a>\n### Update `llhttp`\n\nThe HTTP parser used by `undici` is a WebAssembly build of [`llhttp`](https://github.com/nodejs/llhttp).\n\nWhile the project itself provides a way to compile targeting WebAssembly, at the moment we embed the sources\ndirectly and compile the module in `undici`.\n\nThe `deps/llhttp/include` folder contains the C header files, while the `deps/llhttp/src` folder contains\nthe C source files needed to compile the module.\n\nThe `lib/llhttp` folder contains the `.js` transpiled assets required to implement a parser.\n\nThe following are the steps required to perform an update.\n\n#### Clone the [llhttp](https://github.com/nodejs/llhttp) project\n\n```bash\ngit clone git@github.com:nodejs/llhttp.git\n\ncd llhttp\n```\n\n#### Checkout a `llhttp` release\n\n```bash\ngit checkout <tag>\n```\n\n#### Install the `llhttp` dependencies\n\n```bash\nnpm i\n```\n\n#### Run the wasm build script\n\n> This requires [docker](https://www.docker.com/) installed on your machine.\n\n```bash\nnpm run build-wasm\n```\n\n#### Copy the sources to `undici`\n\n```bash\ncp build/wasm/*.js <your-path-to-undici>/lib/llhttp/\n\ncp build/wasm/*.js.map <your-path-to-undici>/lib/llhttp/\n\ncp build/wasm/*.d.ts <your-path-to-undici>/lib/llhttp/\n\ncp src/native/api.c src/native/http.c build/c/llhttp.c <your-path-to-undici>/deps/llhttp/src/\n\ncp src/native/api.h build/llhttp.h <your-path-to-undici>/deps/llhttp/include/\n```\n\n#### Build the WebAssembly module in `undici`\n\n> This requires [docker](https://www.docker.com/) installed on your machine.\n\n```bash\ncd <your-path-to-undici>\n\nnpm run build:wasm\n```\n\n#### Commit the contents of lib/llhttp\n\nCreate a commit which includes all of the updated files in lib/llhttp.\n\n<a id=\"update-wpts\"></a>\n### Update `WPTs`\n\n`undici` runs a subset of the [`web-platform-tests`](https://github.com/web-platform-tests/wpt).\n\n### Steps:\n\n```bash\ngit submodule update --init --recursive\n```\n\n### Run the tests\n\nRun the tests to ensure that any new failures are marked as such.\n\nBefore running the tests for the first time, you must setup the testing environment.\n```bash\ncd test/web-platform-tests\nnode wpt-runner.mjs setup\n```\n\nTo run all tests:\n\n```bash\nnpm run test:wpt\n```\n\nTo run a subset of tests:\n```bash\ncd test/web-platform-tests\nnode wpt-runner.mjs run [filter] [filterb]\n```\n\nTo run a single file:\n```bash\ncd test/web-platform-tests\nnode wpt-runner.mjs run /path/to/test\n```\n\n### Debugging\n\nVerbose logging can be enabled by setting the [`NODE_DEBUG`](https://nodejs.org/api/cli.html#node_debugmodule) flag:\n\n```bash\nnpx cross-env NODE_DEBUG=UNDICI_WPT node --run test:wpt\n```\n\n(`npx cross-env` can be omitted on Linux and Mac)\n\n<a id=\"lint\"></a>\n### Lint\n\n```bash\nnpm run lint\n```\n\n<a id=\"test\"></a>\n### Test\n\n```bash\nnpm run test\n```\n\n<a id=\"coverage\"></a>\n### Coverage\n\n```bash\nnpm run coverage\n```\n\n<a id=\"releases\"></a>\n### Issuing Releases\n\nRelease is automatic on commit to main which bumps the package.json version field.\nUse the \"Create release PR\" github action to generate a release PR.\n\n<a id=\"external-builds\"></a>\n### Building for externally shared node builtins\n\nIf you are packaging `undici` for a distro, this might help if you would like to use\nan unbundled version instead of bundling one in `libnode.so`.\n\nTo enable this, pass `EXTERNAL_PATH=/path/to/global/node_modules/undici` to `build/wasm.js`.\nPass this path with `loader.js` appended to `--shared-builtin-undici/undici-path` in Node.js's `configure.py`.\nIf building on a non-Alpine Linux distribution, you may need to also set the `WASM_CC`, `WASM_CFLAGS`, `WASM_LDFLAGS` and `WASM_LDLIBS` environment variables before running `build/wasm.js`.\nSimilarly, you can set the `WASM_OPT` environment variable to utilize your own `wasm-opt` optimizer.\n\n<a id=\"benchmarks\"></a>\n### Benchmarks\n\n```bash\ncd benchmarks && npm i && npm run bench\n```\n\nThe benchmarks will be available at `http://localhost:3042`.\n\n<a id=\"documentation\"></a>\n### Documentation\n\n```bash\ncd docs && npm i && npm run serve\n```\n\nThe documentation will be available at `http://localhost:3000`.\n\n<a id=\"developers-certificate-of-origin\"></a>\n## Developer's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n* (a) The contribution was created in whole or in part by me and I\n  have the right to submit it under the open source license\n  indicated in the file; or\n\n* (b) The contribution is based upon previous work that, to the best\n  of my knowledge, is covered under an appropriate open source\n  license and I have the right under that license to submit that\n  work with modifications, whether created in whole or in part\n  by me, under the same open source license (unless I am\n  permitted to submit under a different license), as indicated\n  in the file; or\n\n* (c) The contribution was provided directly to me by some other\n  person who certified (a), (b) or (c) and I have not modified\n  it.\n\n* (d) I understand and agree that this project and the contribution\n  are public and that a record of the contribution (including all\n  personal information I submit with it, including my sign-off) is\n  maintained indefinitely and may be redistributed consistent with\n  this project or the open source license(s) involved.\n\n<a id=\"moderation-policy\"></a>\n### Moderation Policy\n\nThe [Node.js Moderation Policy] applies to this project.\n\n[Node.js Moderation Policy]: https://github.com/nodejs/admin/blob/main/Moderation-Policy.md\n"
  },
  {
    "path": "GOVERNANCE.md",
    "content": "### Undici Working Group\n\nThe Node.js Undici project is governed by a Working Group (WG)\nthat is responsible for high-level guidance of the project.\n\nThe WG has final authority over this project including:\n\n* Technical direction\n* Project governance and process (including this policy)\n* Contribution policy\n* GitHub repository hosting\n* Conduct guidelines\n* Maintaining the list of additional Collaborators\n\nFor the current list of WG members, see the project\n[README.md](./README.md#collaborators).\n\n### Collaborators\n\nThe undici GitHub repository is\nmaintained by the WG and additional Collaborators who are added by the\nWG on an ongoing basis.\n\nIndividuals making significant and valuable contributions are made\nCollaborators and given commit-access to the project. These\nindividuals are identified by the WG and their addition as\nCollaborators is discussed during the WG meeting.\n\n_Note:_ If you make a significant contribution and are not considered\nfor commit-access log an issue or contact a WG member directly and it\nwill be brought up in the next WG meeting.\n\nModifications of the contents of the undici repository are\nmade on\na collaborative basis. Anybody with a GitHub account may propose a\nmodification via pull request and it will be considered by the project\nCollaborators. All pull requests must be reviewed and accepted by a\nCollaborator with sufficient expertise who is able to take full\nresponsibility for the change. In the case of pull requests proposed\nby an existing Collaborator, an additional Collaborator is required\nfor sign-off. Consensus should be sought if additional Collaborators\nparticipate and there is disagreement around a particular\nmodification. See _Consensus Seeking Process_ below for further detail\non the consensus model used for governance.\n\nCollaborators may opt to elevate significant or controversial\nmodifications, or modifications that have not found consensus to the\nWG for discussion by assigning the ***WG-agenda*** tag to a pull\nrequest or issue. The WG should serve as the final arbiter where\nrequired.\n\nFor the current list of Collaborators, see the project\n[README.md](./README.md#collaborators). The list should be in\nalphabetical order.\n\n### WG Membership\n\nWG seats are not time-limited.  There is no fixed size of the WG.\nHowever, the expected target is between 6 and 12, to ensure adequate\ncoverage of important areas of expertise, balanced with the ability to\nmake decisions efficiently.\n\nThere is no specific set of requirements or qualifications for WG\nmembership beyond these rules.\n\nThe WG may add additional members to the WG by unanimous consensus.\n\nA WG member may be removed from the WG by voluntary resignation, or by\nunanimous consensus of all other WG members.\n\nChanges to WG membership should be posted in the agenda, and may be\nsuggested as any other agenda item (see \"WG Meetings\" below).\n\nIf an addition or removal is proposed during a meeting, and the full\nWG is not in attendance to participate, then the addition or removal\nis added to the agenda for the subsequent meeting.  This is to ensure\nthat all members are given the opportunity to participate in all\nmembership decisions.  If a WG member is unable to attend a meeting\nwhere a planned membership decision is being made, then their consent\nis assumed.\n\nNo more than 1/3 of the WG members may be affiliated with the same\nemployer.  If removal or resignation of a WG member, or a change of\nemployment by a WG member, creates a situation where more than 1/3 of\nthe WG membership shares an employer, then the situation must be\nimmediately remedied by the resignation or removal of one or more WG\nmembers affiliated with the over-represented employer(s).\n\n### WG Meetings\n\nThe WG meets occasionally on Zoom. A designated moderator\napproved by the WG runs the meeting. Each meeting should be\npublished to YouTube.\n\nItems are added to the WG agenda that are considered contentious or\nare modifications of governance, contribution policy, WG membership,\nor release process.\n\nThe intention of the agenda is not to approve or review all patches;\nthat should happen continuously on GitHub and be handled by the larger\ngroup of Collaborators.\n\nAny community member or contributor can ask that something be added to\nthe next meeting's agenda by logging a GitHub Issue. Any Collaborator,\nWG member or the moderator can add the item to the agenda by adding\nthe ***WG-agenda*** tag to the issue.\n\nPrior to each WG meeting the moderator will share the Agenda with\nmembers of the WG. WG members can add any items they like to the\nagenda at the beginning of each meeting. The moderator and the WG\ncannot veto or remove items.\n\nThe WG may invite persons or representatives from certain projects to\nparticipate in a non-voting capacity.\n\nThe moderator is responsible for summarizing the discussion of each\nagenda item and sends it as a pull request after the meeting.\n\n### Consensus Seeking Process\n\nThe WG follows a\n[Consensus\nSeeking](http://en.wikipedia.org/wiki/Consensus-seeking_decision-making)\ndecision-making model.\n\nWhen an agenda item has appeared to reach a consensus the moderator\nwill ask \"Does anyone object?\" as a final call for dissent from the\nconsensus.\n\nIf an agenda item cannot reach a consensus a WG member can call for\neither a closing vote or a vote to table the issue to the next\nmeeting. The call for a vote must be seconded by a majority of the WG\nor else the discussion will continue. Simple majority wins.\n\nNote that changes to WG membership require a majority consensus.  See\n\"WG Membership\" above.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) Matteo Collina and Undici contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nThis document details any and all processes relevant to project maintainers. Maintainers should feel empowered to contribute back to this document with any process changes they feel improve the overall experience for themselves and other maintainers.\n\n## Labels\n\nMaintainers are encouraged to use the extensive and detailed list of labels for easier repo management.\n\n* Generally, all issues should be labelled. The most general labels are `bug`, `enhancement`, and `Status: help-wanted`.\n* Issues specific to a certain aspect of the project should be labeled using one of the specificity labels listed below. For example, a bug in the `Client` class should have the `Client` and `bug` label assigned.\n  * Specificity labels:\n    * `Agent`\n    * `Client`\n    * `Docs`\n    * `Performance`\n    * `Pool`\n    * `Tests`\n    * `Types`\n* Any `question` or `usage help` issues should be converted into Q&A Discussions\n* `Status:` labels should be added to all open issues indicating their relative development status.\n  * Status labels:\n    * `Status: blocked`\n    * `Status: help-wanted`\n    * `Status: in-progress`\n    * `Status: wontfix`\n* Issues and/or pull requests with an agreed upon semver status can be assigned the appropriate `semver-` label.\n  * Semver labels:\n    * `semver-major`\n    * `semver-minor`\n    * `semver-patch`\n* Issues with a low-barrier of entry should be assigned the `good first issue` label.\n* Do not use the `invalid` label, instead use `bug` or `Status: wontfix`.\n* Duplicate issues should initially be assigned the `duplicate` label.\n\n\n## Making a Release\n\n1. Go to github actions, then select [\"Create Release PR\"](https://github.com/nodejs/undici/actions/workflows/release-create-pr.yml).\n2. Run the workflow, selecting `main` and indicating if you want a specific version number or a patch/minor/major release\n3. Wait for the PR to be created. Approve the PR ([this](https://github.com/nodejs/undici/pull/4021) is a an example).\n4. Land the PR, wait for the CI to pass.\n5. Got to the [\"Release\"](https://github.com/nodejs/undici/actions/workflows/release.yml) workflow, you should see a job waiting.\n6. If you are one of the [releases](https://github.com/nodejs/undici?tab=readme-ov-file#releasers), then click \"review deployments\", then select \"release\" and click \"approve and deploy\". If you are not a releaser, contact one.\n"
  },
  {
    "path": "README.md",
    "content": "# undici\n\n[![Node CI](https://github.com/nodejs/undici/actions/workflows/ci.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![neostandard javascript style](https://img.shields.io/badge/neo-standard-7fffff?style=flat\\&labelColor=ff80ff)](https://github.com/neostandard/neostandard) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)\n\nAn HTTP/1.1 client, written from scratch for Node.js.\n\n> Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici.\nIt is also a Stranger Things reference.\n\n## How to get involved\n\nHave a question about using Undici? Open a [Q&A Discussion](https://github.com/nodejs/undici/discussions/new) or join our official OpenJS [Slack](https://openjs-foundation.slack.com/archives/C01QF9Q31QD) channel.\n\nLooking to contribute? Start by reading the [contributing guide](./CONTRIBUTING.md)\n\n## Install\n\n```\nnpm i undici\n```\n\n## Benchmarks\n\nThe benchmark is a simple getting data [example](https://github.com/nodejs/undici/blob/main/benchmarks/benchmark.js) using a\n50 TCP connections with a pipelining depth of 10 running on Node 22.11.0.\n\n```\n┌────────────────────────┬─────────┬────────────────────┬────────────┬─────────────────────────┐\n│  Tests                 │ Samples │ Result             │ Tolerance  │ Difference with slowest │\n├────────────────────────┼─────────┼────────────────────┼────────────┼─────────────────────────┤\n│  'axios'               │ 15      │ '5708.26 req/sec'  │ '± 2.91 %' │ '-'                     │\n│  'http - no keepalive' │ 10      │ '5809.80 req/sec'  │ '± 2.30 %' │ '+ 1.78 %'              │\n│  'request'             │ 30      │ '5828.80 req/sec'  │ '± 2.91 %' │ '+ 2.11 %'              │\n│  'undici - fetch'      │ 40      │ '5903.78 req/sec'  │ '± 2.87 %' │ '+ 3.43 %'              │\n│  'node-fetch'          │ 10      │ '5945.40 req/sec'  │ '± 2.13 %' │ '+ 4.15 %'              │\n│  'got'                 │ 35      │ '6511.45 req/sec'  │ '± 2.84 %' │ '+ 14.07 %'             │\n│  'http - keepalive'    │ 65      │ '9193.24 req/sec'  │ '± 2.92 %' │ '+ 61.05 %'             │\n│  'superagent'          │ 35      │ '9339.43 req/sec'  │ '± 2.95 %' │ '+ 63.61 %'             │\n│  'undici - pipeline'   │ 50      │ '13364.62 req/sec' │ '± 2.93 %' │ '+ 134.13 %'            │\n│  'undici - stream'     │ 95      │ '18245.36 req/sec' │ '± 2.99 %' │ '+ 219.63 %'            │\n│  'undici - request'    │ 50      │ '18340.17 req/sec' │ '± 2.84 %' │ '+ 221.29 %'            │\n│  'undici - dispatch'   │ 40      │ '22234.42 req/sec' │ '± 2.94 %' │ '+ 289.51 %'            │\n└────────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘\n```\n\n## Undici vs. Fetch\n\n### Overview\n\nNode.js includes a built-in `fetch()` implementation powered by undici starting from Node.js v18. However, there are important differences between using the built-in fetch and installing undici as a separate module.\n\n### Built-in Fetch (Node.js v18+)\n\nNode.js's built-in fetch is powered by a bundled version of undici:\n\n```js\n// Available globally in Node.js v18+\nconst response = await fetch('https://api.example.com/data');\nconst data = await response.json();\n\n// Check the bundled undici version\nconsole.log(process.versions.undici); // e.g., \"5.28.4\"\n```\n\n**Pros:**\n- No additional dependencies required\n- Works across different JavaScript runtimes\n- Automatic compression handling (gzip, deflate, br)\n- Built-in caching support (in development)\n\n**Cons:**\n- Limited to the undici version bundled with your Node.js version\n- Less control over connection pooling and advanced features\n- Error handling follows Web API standards (errors wrapped in `TypeError`)\n- Performance overhead due to Web Streams implementation\n\n### Undici Module\n\nInstalling undici as a separate module gives you access to the latest features and APIs:\n\n```bash\nnpm install undici\n```\n\n```js\nimport { request, fetch, Agent, setGlobalDispatcher } from 'undici';\n\n// Use undici.request for maximum performance\nconst { statusCode, headers, body } = await request('https://api.example.com/data');\nconst data = await body.json();\n\n// Or use undici.fetch with custom configuration\nconst agent = new Agent({ keepAliveTimeout: 10000 });\nsetGlobalDispatcher(agent);\nconst response = await fetch('https://api.example.com/data');\n```\n\n**Pros:**\n- Latest undici features and bug fixes\n- Access to advanced APIs (`request`, `stream`, `pipeline`)\n- Fine-grained control over connection pooling\n- Better error handling with clearer error messages\n- Superior performance, especially with `undici.request`\n- HTTP/1.1 pipelining support\n- Custom interceptors and middleware\n- Advanced features like `ProxyAgent`, `Socks5Agent`, `MockAgent`\n\n**Cons:**\n- Additional dependency to manage\n- Larger bundle size\n\n### When to Use Each\n\n#### Use Built-in Fetch When:\n- You want zero dependencies\n- Building isomorphic code that runs in browsers and Node.js\n- Publishing to npm and want to maximize compatibility with JS runtimes\n- Simple HTTP requests without advanced configuration\n- You're publishing to npm and you want to maximize compatiblity\n- You don't depend on features from a specific version of undici\n\n#### Use Undici Module When:\n- You need the latest undici features and performance improvements\n- You require advanced connection pooling configuration\n- You need APIs not available in the built-in fetch (`ProxyAgent`, `Socks5Agent`, `MockAgent`, etc.)\n- Performance is critical (use `undici.request` for maximum speed)\n- You want better error handling and debugging capabilities\n- You need HTTP/1.1 pipelining or advanced interceptors\n- You prefer decoupled protocol and API interfaces\n\n### Performance Comparison\n\nBased on benchmarks, here's the typical performance hierarchy:\n\n1. **`undici.request()`** - Fastest, most efficient\n2. **`undici.fetch()`** - Good performance, standard compliance\n3. **Node.js `http`/`https`** - Baseline performance\n\n### Migration Guide\n\nIf you're currently using built-in fetch and want to migrate to undici:\n\n```js\n// Before: Built-in fetch\nconst response = await fetch('https://api.example.com/data');\n\n// After: Undici fetch (drop-in replacement)\nimport { fetch } from 'undici';\nconst response = await fetch('https://api.example.com/data');\n\n// Or: Undici request (better performance)\nimport { request } from 'undici';\nconst { statusCode, body } = await request('https://api.example.com/data');\nconst data = await body.json();\n```\n\n### Version Compatibility\n\nYou can check which version of undici is bundled with your Node.js version:\n\n```js\nconsole.log(process.versions.undici);\n```\n\nInstalling undici as a module allows you to use a newer version than what's bundled with Node.js, giving you access to the latest features and performance improvements.\n\n## Quick Start\n\n### Basic Request\n\n```js\nimport { request } from 'undici'\n\nconst {\n  statusCode,\n  headers,\n  trailers,\n  body\n} = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode)\nconsole.log('headers', headers)\n\nfor await (const data of body) { console.log('data', data) }\n\nconsole.log('trailers', trailers)\n```\n\n### Using Cache Interceptor\n\nUndici provides a powerful HTTP caching interceptor that follows HTTP caching best practices. Here's how to use it:\n\n```js\nimport { fetch, Agent, interceptors, cacheStores } from 'undici';\n\n// Create a client with cache interceptor\nconst client = new Agent().compose(interceptors.cache({\n  // Optional: Configure cache store (defaults to MemoryCacheStore)\n  store: new cacheStores.MemoryCacheStore({\n    maxSize: 100 * 1024 * 1024, // 100MB\n    maxCount: 1000,\n    maxEntrySize: 5 * 1024 * 1024 // 5MB\n  }),\n  \n  // Optional: Specify which HTTP methods to cache (default: ['GET', 'HEAD'])\n  methods: ['GET', 'HEAD']\n}));\n\n// Set the global dispatcher to use our caching client\nsetGlobalDispatcher(client);\n\n// Now all fetch requests will use the cache\nasync function getData() {\n  const response = await fetch('https://api.example.com/data');\n  // The server should set appropriate Cache-Control headers in the response\n  // which the cache will respect based on the cache policy\n  return response.json();\n}\n\n// First request - fetches from origin\nconst data1 = await getData();\n\n// Second request - served from cache if within max-age\nconst data2 = await getData();\n```\n\n#### Key Features:\n- **Automatic Caching**: Respects `Cache-Control` and `Expires` headers\n- **Validation**: Supports `ETag` and `Last-Modified` validation\n- **Storage Options**: In-memory or persistent SQLite storage\n- **Flexible**: Configure cache size, TTL, and more\n\n## Global Installation\n\nUndici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally:\n\n```js\nimport { install } from 'undici'\n\n// Install all WHATWG fetch classes globally\ninstall()\n\n// Now you can use fetch classes globally without importing\nconst response = await fetch('https://api.example.com/data')\nconst data = await response.json()\n\n// All classes are available globally:\nconst headers = new Headers([['content-type', 'application/json']])\nconst request = new Request('https://example.com')\nconst formData = new FormData()\nconst ws = new WebSocket('wss://example.com')\nconst eventSource = new EventSource('https://example.com/events')\n```\n\nThe `install()` function adds the following classes to `globalThis`:\n\n- `fetch` - The fetch function\n- `Headers` - HTTP headers management\n- `Response` - HTTP response representation\n- `Request` - HTTP request representation\n- `FormData` - Form data handling\n- `WebSocket` - WebSocket client\n- `CloseEvent`, `ErrorEvent`, `MessageEvent` - WebSocket events\n- `EventSource` - Server-sent events client\n\nThis is useful for:\n- Polyfilling environments that don't have fetch\n- Ensuring consistent fetch behavior across different Node.js versions\n- Making undici's implementations available globally for libraries that expect them\n\n## Body Mixins\n\nThe `body` mixins are the most common way to format the request/response body. Mixins include:\n\n- [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)\n- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)\n- [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)\n- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)\n- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)\n\n> [!NOTE]\n> The body returned from `undici.request` does not implement `.formData()`.\n\nExample usage:\n\n```js\nimport { request } from 'undici'\n\nconst {\n  statusCode,\n  headers,\n  trailers,\n  body\n} = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode)\nconsole.log('headers', headers)\nconsole.log('data', await body.json())\nconsole.log('trailers', trailers)\n```\n\n_Note: Once a mixin has been called then the body cannot be reused, thus calling additional mixins on `.body`, e.g. `.body.json(); .body.text()` will result in an error `TypeError: unusable` being thrown and returned through the `Promise` rejection._\n\nShould you need to access the `body` in plain-text after using a mixin, the best practice is to use the `.text()` mixin first and then manually parse the text to the desired format.\n\nFor more information about their behavior, please reference the body mixin from the [Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).\n\n## Common API Methods\n\nThis section documents our most commonly used API methods. Additional APIs are documented in their own files within the [docs](./docs/) folder and are accessible via the navigation list on the left side of the docs site.\n\n### `undici.request([url, options]): Promise`\n\nArguments:\n\n* **url** `string | URL | UrlObject`\n* **options** [`RequestOptions`](./docs/docs/api/Dispatcher.md#parameter-requestoptions)\n  * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)\n  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`\n\nReturns a promise with the result of the `Dispatcher.request` method.\n\nCalls `options.dispatcher.request(options)`.\n\nSee [Dispatcher.request](./docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./docs/examples/README.md) for examples.\n\n### `undici.stream([url, options, ]factory): Promise`\n\nArguments:\n\n* **url** `string | URL | UrlObject`\n* **options** [`StreamOptions`](./docs/docs/api/Dispatcher.md#parameter-streamoptions)\n  * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)\n  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`\n* **factory** `Dispatcher.stream.factory`\n\nReturns a promise with the result of the `Dispatcher.stream` method.\n\nCalls `options.dispatcher.stream(options, factory)`.\n\nSee [Dispatcher.stream](./docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.\n\n### `undici.pipeline([url, options, ]handler): Duplex`\n\nArguments:\n\n* **url** `string | URL | UrlObject`\n* **options** [`PipelineOptions`](./docs/docs/api/Dispatcher.md#parameter-pipelineoptions)\n  * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)\n  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`\n* **handler** `Dispatcher.pipeline.handler`\n\nReturns: `stream.Duplex`\n\nCalls `options.dispatch.pipeline(options, handler)`.\n\nSee [Dispatcher.pipeline](./docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.\n\n### `undici.connect([url, options]): Promise`\n\nStarts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).\n\nArguments:\n\n* **url** `string | URL | UrlObject`\n* **options** [`ConnectOptions`](./docs/docs/api/Dispatcher.md#parameter-connectoptions)\n  * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)\n* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)\n\nReturns a promise with the result of the `Dispatcher.connect` method.\n\nCalls `options.dispatch.connect(options)`.\n\nSee [Dispatcher.connect](./docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.\n\n### `undici.fetch(input[, init]): Promise`\n\nImplements [fetch](https://fetch.spec.whatwg.org/#fetch-method).\n\n* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch\n* https://fetch.spec.whatwg.org/#fetch-method\n\nBasic usage example:\n\n```js\nimport { fetch } from 'undici'\n\n\nconst res = await fetch('https://example.com')\nconst json = await res.json()\nconsole.log(json)\n```\n\nYou can pass an optional dispatcher to `fetch` as:\n\n```js\nimport { fetch, Agent } from 'undici'\n\nconst res = await fetch('https://example.com', {\n  // Mocks are also supported\n  dispatcher: new Agent({\n    keepAliveTimeout: 10,\n    keepAliveMaxTimeout: 10\n  })\n})\nconst json = await res.json()\nconsole.log(json)\n```\n\n#### `request.body`\n\nA body can be of the following types:\n\n- ArrayBuffer\n- ArrayBufferView\n- AsyncIterables\n- Blob\n- Iterables\n- String\n- URLSearchParams\n- FormData\n\nIn this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard](https://fetch.spec.whatwg.org).\n\n```js\nimport { fetch } from 'undici'\n\nconst data = {\n  async *[Symbol.asyncIterator]() {\n    yield 'hello'\n    yield 'world'\n  },\n}\n\nawait fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })\n```\n\n[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) besides text data and buffers can also utilize streams via [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects:\n\n```js\nimport { openAsBlob } from 'node:fs'\n\nconst file = await openAsBlob('./big.csv')\nconst body = new FormData()\nbody.set('file', file, 'big.csv')\n\nawait fetch('http://example.com', { method: 'POST', body })\n```\n\n#### `request.duplex`\n\n- `'half'`\n\nIn this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`, however, even though the value must be set to `'half'`, it is actually a _full_ duplex. For more detail refer to the [Fetch Standard](https://fetch.spec.whatwg.org/#dom-requestinit-duplex).\n\n#### `response.body`\n\nNodejs has two kinds of streams: [web streams](https://nodejs.org/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.\n\n```js\nimport { fetch } from 'undici'\nimport { Readable } from 'node:stream'\n\nconst response = await fetch('https://example.com')\nconst readableWebStream = response.body\nconst readableNodeStream = Readable.fromWeb(readableWebStream)\n```\n\n## Specification Compliance\n\nThis section documents parts of the [HTTP/1.1](https://www.rfc-editor.org/rfc/rfc9110.html) and [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does\nnot support or does not fully implement.\n\n#### CORS\n\nUnlike browsers, Undici does not implement CORS (Cross-Origin Resource Sharing) checks by default. This means:\n\n- No preflight requests are automatically sent for cross-origin requests\n- No validation of `Access-Control-Allow-Origin` headers is performed\n- Requests to any origin are allowed regardless of the source\n\nThis behavior is intentional for server-side environments where CORS restrictions are typically unnecessary. If your application requires CORS-like protections, you will need to implement these checks manually.\n\n#### Garbage Collection\n\n* https://fetch.spec.whatwg.org/#garbage-collection\n\nThe [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on\n[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources.\n\nGarbage collection in Node is less aggressive and deterministic\n(due to the lack of clear idle periods that browsers have through the rendering refresh rate)\nwhich means that leaving the release of connection resources to the garbage collector can lead\nto excessive connection usage, reduced performance (due to less connection re-use), and even\nstalls or deadlocks when running out of connections.\nTherefore, __it is important to always either consume or cancel the response body anyway__.\n\n```js\n// Do\nconst { body, headers } = await fetch(url);\nfor await (const chunk of body) {\n  // force consumption of body\n}\n\n// Do not\nconst { headers } = await fetch(url);\n```\n\nHowever, if you want to get only headers, it might be better to use `HEAD` request method. Usage of this method will obviate the need for consumption or cancelling of the response body. See [MDN - HTTP - HTTP request methods - HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) for more details.\n\n```js\nconst headers = await fetch(url, { method: 'HEAD' })\n  .then(res => res.headers)\n```\n\nNote that consuming the response body is _mandatory_ for `request`:\n\n```js\n// Do\nconst { body, headers } = await request(url);\nawait body.dump(); // force consumption of body\n\n// Do not\nconst { headers } = await request(url);\n```\n\n#### Forbidden and Safelisted Header Names\n\n* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name\n* https://fetch.spec.whatwg.org/#forbidden-header-name\n* https://fetch.spec.whatwg.org/#forbidden-response-header-name\n* https://github.com/wintercg/fetch/issues/6\n\nThe [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user.\n\n#### Content-Encoding\n\n* https://www.rfc-editor.org/rfc/rfc9110#field.content-encoding\n\nUndici limits the number of `Content-Encoding` layers in a response to **5** to prevent resource exhaustion attacks. If a server responds with more than 5 content-encodings (e.g., `Content-Encoding: gzip, gzip, gzip, gzip, gzip, gzip`), the fetch will be rejected with an error. This limit matches the approach taken by [curl](https://curl.se/docs/CVE-2022-32206.html) and [urllib3](https://github.com/advisories/GHSA-gm62-xv2j-4rw9).\n\n#### `undici.upgrade([url, options]): Promise`\n\nUpgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.\n\nArguments:\n\n* **url** `string | URL | UrlObject`\n* **options** [`UpgradeOptions`](./docs/docs/api/Dispatcher.md#parameter-upgradeoptions)\n  * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)\n* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)\n\nReturns a promise with the result of the `Dispatcher.upgrade` method.\n\nCalls `options.dispatcher.upgrade(options)`.\n\nSee [Dispatcher.upgrade](./docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.\n\n### `undici.setGlobalDispatcher(dispatcher)`\n\n* dispatcher `Dispatcher`\n\nSets the global dispatcher used by Common API Methods. Global dispatcher is shared among compatible undici modules,\nincluding undici that is bundled internally with node.js.\n\n### `undici.getGlobalDispatcher()`\n\nGets the global dispatcher used by Common API Methods.\n\nReturns: `Dispatcher`\n\n### `undici.setGlobalOrigin(origin)`\n\n* origin `string | URL | undefined`\n\nSets the global origin used in `fetch`.\n\nIf `undefined` is passed, the global origin will be reset. This will cause `Response.redirect`, `new Request()`, and `fetch` to throw an error when a relative path is passed.\n\n```js\nsetGlobalOrigin('http://localhost:3000')\n\nconst response = await fetch('/api/ping')\n\nconsole.log(response.url) // http://localhost:3000/api/ping\n```\n\n### `undici.getGlobalOrigin()`\n\nGets the global origin used in `fetch`.\n\nReturns: `URL`\n\n### `UrlObject`\n\n* **port** `string | number` (optional)\n* **path** `string` (optional)\n* **pathname** `string` (optional)\n* **hostname** `string` (optional)\n* **origin** `string` (optional)\n* **protocol** `string` (optional)\n* **search** `string` (optional)\n\n#### Expect\n\nUndici does not support the `Expect` request header field. The request\nbody is  always immediately sent and the `100 Continue` response will be\nignored.\n\nRefs: https://tools.ietf.org/html/rfc7231#section-5.1.1\n\n#### Pipelining\n\nUndici will only use pipelining if configured with a `pipelining` factor\ngreater than `1`. Also it is important to pass `blocking: false` to the\nrequest options to properly pipeline requests.\n\nUndici always assumes that connections are persistent and will immediately\npipeline requests, without checking whether the connection is persistent.\nHence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is\nnot supported.\n\nUndici will immediately pipeline when retrying requests after a failed\nconnection. However, Undici will not retry the first remaining requests in\nthe prior pipeline and instead error the corresponding callback/promise/stream.\n\nUndici will abort all running requests in the pipeline when any of them are\naborted.\n\n* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2\n* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2\n\n#### Manual Redirect\n\nSince it is not possible to manually follow an HTTP redirect on the server-side,\nUndici returns the actual response instead of an `opaqueredirect` filtered one\nwhen invoked with a `manual` redirect. This aligns `fetch()` with the other\nimplementations in Deno and Cloudflare Workers.\n\nRefs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling\n\n### Workarounds\n\n#### Network address family autoselection.\n\nIf you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)\nfirst, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case\nundici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.\n\nIf the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version\n(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`\nand `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.\n\n## Collaborators\n\n* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup>\n* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>\n* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>\n* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>\n* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>\n* [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak>\n\n## Past Collaborators\n* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor>\n\n### Releasers\n\n* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>\n* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>\n* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>\n* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>\n\n## Long Term Support\n\nUndici aligns with the Node.js LTS schedule. The following table shows the supported versions:\n\n| Undici Version | Bundled in Node.js | Node.js Versions Supported | End of Life |\n|----------------|-------------------|----------------------------|-------------|\n| 5.x           | 18.x              | ≥14.0 (tested: 14, 16, 18) | 2024-04-30  |\n| 6.x           | 20.x, 22.x       | ≥18.17 (tested: 18, 20, 21, 22) | 2026-04-30  |\n| 7.x           | 24.x              | ≥20.18.1 (tested: 20, 22, 24) | 2027-04-30  |\n\n## License\n\nMIT\n"
  },
  {
    "path": "SECURITY.md",
    "content": "If you believe you have found a security issue in the software in this\nrepository, please consult https://github.com/nodejs/node/blob/HEAD/SECURITY.md.\n"
  },
  {
    "path": "benchmarks/_util/index.js",
    "content": "'use strict'\n\nconst parallelRequests = parseInt(process.env.PARALLEL, 10) || 100\n\nfunction makeParallelRequests (cb) {\n  const promises = new Array(parallelRequests)\n  for (let i = 0; i < parallelRequests; ++i) {\n    promises[i] = new Promise(cb)\n  }\n  return Promise.all(promises)\n}\n\nfunction printResults (results) {\n  // Sort results by least performant first, then compare relative performances and also printing padding\n  let last\n\n  const rows = Object.entries(results)\n    // If any failed, put on the top of the list, otherwise order by mean, ascending\n    .sort((a, b) => (!a[1].success ? -1 : b[1].mean - a[1].mean))\n    .map(([name, result]) => {\n      if (!result.success) {\n        return {\n          Tests: name,\n          Samples: result.size,\n          Result: 'Errored',\n          Tolerance: 'N/A',\n          'Difference with Slowest': 'N/A'\n        }\n      }\n\n      // Calculate throughput and relative performance\n      const { size, mean, standardError } = result\n      const relative = last !== 0 ? (last / mean - 1) * 100 : 0\n\n      // Save the slowest for relative comparison\n      if (typeof last === 'undefined') {\n        last = mean\n      }\n\n      return {\n        Tests: name,\n        Samples: size,\n        Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,\n        Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,\n        'Difference with slowest':\n          relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'\n      }\n    })\n\n  return console.table(rows)\n}\n\n/**\n * @param {number} num\n * @returns {string}\n */\nfunction formatBytes (num) {\n  if (!Number.isFinite(num)) {\n    throw new Error('invalid number')\n  }\n\n  const prefixes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']\n\n  const idx = Math.min(Math.floor(Math.log(num) / Math.log(1024)), prefixes.length - 1)\n\n  return `${(num / Math.pow(1024, idx)).toFixed(2)}${prefixes[idx]}`\n}\n\nmodule.exports = { makeParallelRequests, printResults, formatBytes }\n"
  },
  {
    "path": "benchmarks/_util/runner.js",
    "content": "// @ts-check\n\n'use strict'\n\nclass Info {\n  /** @type {string} */\n  #name\n  /** @type {bigint} */\n  #current\n  /** @type {bigint} */\n  #finish\n  /** @type {(...args: any[]) => any} */\n  #callback\n  /** @type {boolean} */\n  #finalized = false\n\n  /**\n   * @param {string} name\n   * @param {(...args: any[]) => any} callback\n   */\n  constructor (name, callback) {\n    this.#name = name\n    this.#callback = callback\n  }\n\n  get name () {\n    return this.#name\n  }\n\n  start () {\n    if (this.#finalized) {\n      throw new TypeError('called after finished.')\n    }\n    this.#current = process.hrtime.bigint()\n  }\n\n  end () {\n    if (this.#finalized) {\n      throw new TypeError('called after finished.')\n    }\n    this.#finish = process.hrtime.bigint()\n    this.#finalized = true\n    this.#callback()\n  }\n\n  diff () {\n    return Number(this.#finish - this.#current)\n  }\n}\n\n/**\n * @typedef BenchMarkHandler\n * @type {(ev: { name: string; start(): void; end(): void; }) => any}\n */\n\n/**\n * @param {Record<string, BenchMarkHandler>} experiments\n * @param {{ minSamples?: number; maxSamples?: number }} [options]\n * @returns {Promise<{ name: string; average: number; samples: number; fn: BenchMarkHandler; iterationPerSecond: number; min: number; max: number }[]>}\n */\nasync function bench (experiments, options = {}) {\n  const names = Object.keys(experiments)\n\n  /** @type {{ name: string; average: number; samples: number; fn: BenchMarkHandler; iterationPerSecond: number; min: number; max: number }[]} */\n  const results = []\n\n  async function waitMaybePromiseLike (p) {\n    if (\n      (typeof p === 'object' || typeof p === 'function') &&\n      p !== null &&\n      typeof p.then === 'function'\n    ) {\n      await p\n    }\n  }\n\n  for (let i = 0; i < names.length; ++i) {\n    const name = names[i]\n    const fn = experiments[name]\n    const samples = []\n\n    for (let i = 0; i < 8; ++i) {\n      // warmup\n      await new Promise((resolve, reject) => {\n        const info = new Info(name, resolve)\n\n        try {\n          const p = fn(info)\n\n          waitMaybePromiseLike(p).catch((err) => reject(err))\n        } catch (err) {\n          reject(err)\n        }\n      })\n    }\n\n    let timing = 0\n    const minSamples = options.minSamples ?? 128\n\n    for (let j = 0; (j < minSamples || timing < 800_000_000) && (typeof options.maxSamples === 'number' ? options.maxSamples > j : true); ++j) {\n      let resolve = (value) => {}\n      let reject = (reason) => {}\n      const promise = new Promise(\n        (_resolve, _reject) => { resolve = _resolve; reject = _reject }\n      )\n\n      const info = new Info(name, resolve)\n\n      try {\n        const p = fn(info)\n\n        await waitMaybePromiseLike(p)\n      } catch (err) {\n        reject(err)\n      }\n\n      await promise\n\n      samples.push({ time: info.diff() })\n\n      timing += info.diff()\n    }\n\n    const average =\n      samples.map((v) => v.time).reduce((a, b) => a + b, 0) / samples.length\n\n    results.push({\n      name: names[i],\n      average,\n      samples: samples.length,\n      fn,\n      iterationPerSecond: 1e9 / average,\n      min: samples.reduce((a, acc) => Math.min(a, acc.time), samples[0].time),\n      max: samples.reduce((a, acc) => Math.max(a, acc.time), samples[0].time)\n    })\n  }\n\n  return results\n}\n\nmodule.exports = { bench }\n"
  },
  {
    "path": "benchmarks/benchmark-http2.js",
    "content": "'use strict'\n\nconst os = require('node:os')\nconst path = require('node:path')\nconst http2 = require('node:http2')\nconst { readFileSync } = require('node:fs')\nconst { Writable } = require('node:stream')\nconst { isMainThread } = require('node:worker_threads')\n\nconst { Pool, Client, fetch, Agent, setGlobalDispatcher } = require('..')\n\nconst ca = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'ca.pem'), 'utf8')\nconst servername = 'agent1'\n\nconst iterations = (parseInt(process.env.SAMPLES, 10) || 10) + 1\nconst errorThreshold = parseInt(process.env.ERROR_THRESHOLD, 10) || 3\nconst connections = parseInt(process.env.CONNECTIONS, 10) || 50\nconst pipelining = parseInt(process.env.PIPELINING, 10) || 10\nconst parallelRequests = parseInt(process.env.PARALLEL, 10) || 100\nconst headersTimeout = parseInt(process.env.HEADERS_TIMEOUT, 10) || 0\nconst bodyTimeout = parseInt(process.env.BODY_TIMEOUT, 10) || 0\nconst dest = {}\n\nif (process.env.PORT) {\n  dest.port = process.env.PORT\n  dest.url = `https://localhost:${process.env.PORT}`\n} else {\n  dest.url = 'https://localhost'\n  dest.socketPath = path.join(os.tmpdir(), 'undici.sock')\n}\n\nconst httpsBaseOptions = {\n  ca,\n  servername,\n  protocol: 'https:',\n  hostname: 'localhost',\n  method: 'GET',\n  path: '/',\n  query: {\n    frappucino: 'muffin',\n    goat: 'scone',\n    pond: 'moose',\n    foo: ['bar', 'baz', 'bal'],\n    bool: true,\n    numberKey: 256\n  },\n  ...dest\n}\n\nconst undiciOptions = {\n  path: '/',\n  method: 'GET',\n  headersTimeout,\n  bodyTimeout\n}\n\nconst http2NativeClient = http2.connect(httpsBaseOptions.url, {\n  rejectUnauthorized: false\n})\n\nconst Class = connections > 1 ? Pool : Client\nconst dispatcher = new Class(httpsBaseOptions.url, {\n  allowH2: true,\n  pipelining,\n  connections,\n  connect: {\n    rejectUnauthorized: false,\n    ca,\n    servername\n  },\n  ...dest\n})\n\nsetGlobalDispatcher(new Agent({\n  allowH2: true,\n  pipelining,\n  connections,\n  connect: {\n    rejectUnauthorized: false,\n    ca,\n    servername\n  }\n}))\n\nclass SimpleRequest {\n  constructor (resolve) {\n    this.dst = new Writable({\n      write (chunk, encoding, callback) {\n        callback()\n      }\n    }).on('finish', resolve)\n  }\n\n  onConnect (abort) { }\n\n  onHeaders (statusCode, headers, resume) {\n    this.dst.on('drain', resume)\n  }\n\n  onData (chunk) {\n    return this.dst.write(chunk)\n  }\n\n  onComplete () {\n    this.dst.end()\n  }\n\n  onError (err) {\n    throw err\n  }\n}\n\nfunction makeParallelRequests (cb) {\n  const res = Promise.all(Array.from(Array(parallelRequests)).map(() => new Promise(cb)))\n  res.catch(console.error)\n  return res\n}\n\nfunction printResults (results) {\n  // Sort results by least performant first, then compare relative performances and also printing padding\n  let last\n\n  const rows = Object.entries(results)\n    // If any failed, put on the top of the list, otherwise order by mean, ascending\n    .sort((a, b) => (!a[1].success ? -1 : b[1].mean - a[1].mean))\n    .map(([name, result]) => {\n      if (!result.success) {\n        return {\n          Tests: name,\n          Samples: result.size,\n          Result: 'Errored',\n          Tolerance: 'N/A',\n          'Difference with Slowest': 'N/A'\n        }\n      }\n\n      // Calculate throughput and relative performance\n      const { size, mean, standardError } = result\n      const relative = last !== 0 ? (last / mean - 1) * 100 : 0\n\n      // Save the slowest for relative comparison\n      if (typeof last === 'undefined') {\n        last = mean\n      }\n\n      console.log(mean)\n\n      return {\n        Tests: name,\n        Samples: size,\n        Result: `${((1e9 * parallelRequests) / mean).toFixed(2)} req/sec`,\n        Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,\n        'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'\n      }\n    })\n\n  return console.table(rows)\n}\n\nconst experiments = {\n  'native - http2' () {\n    return makeParallelRequests(resolve => {\n      const stream = http2NativeClient.request({\n        [http2.constants.HTTP2_HEADER_PATH]: httpsBaseOptions.path,\n        [http2.constants.HTTP2_HEADER_METHOD]: httpsBaseOptions.method\n      })\n\n      stream.end().on('response', () => {\n        stream.pipe(\n          new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        )\n          .on('error', (err) => {\n            console.log('http2 - request - response - error', err)\n          })\n          .on('finish', () => {\n            resolve()\n          })\n      })\n    })\n  },\n  'undici - pipeline' () {\n    return makeParallelRequests(resolve => {\n      dispatcher\n        .pipeline(undiciOptions, data => {\n          return data.body\n        })\n        .end()\n        .pipe(\n          new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        )\n        .on('finish', resolve)\n    })\n  },\n  'undici - request' () {\n    return makeParallelRequests(resolve => {\n      try {\n        dispatcher.request(undiciOptions).then(({ body }) => {\n          body\n            .pipe(\n              new Writable({\n                write (chunk, encoding, callback) {\n                  callback()\n                }\n              })\n            )\n            .on('error', (err) => {\n              console.log('undici - request - dispatcher.request - body - error', err)\n            })\n            .on('finish', () => {\n              resolve()\n            })\n        })\n      } catch (err) {\n        console.error('undici - request - dispatcher.request - requestCount', err)\n      }\n    })\n  },\n  'undici - stream' () {\n    return makeParallelRequests(resolve => {\n      return dispatcher\n        .stream(undiciOptions, () => {\n          return new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        })\n        .then(resolve)\n    })\n  },\n  'undici - dispatch' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.dispatch(undiciOptions, new SimpleRequest(resolve))\n    })\n  }\n}\n\nif (process.env.PORT) {\n  // fetch does not support the socket\n  experiments['undici - fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      fetch(dest.url, {}).then(res => {\n        res.body.pipeTo(new WritableStream({ write () { }, close () { resolve() } }))\n      }).catch(console.log)\n    })\n  }\n}\n\nasync function main () {\n  const { cronometro } = await import('cronometro')\n\n  cronometro(\n    experiments,\n    {\n      iterations,\n      errorThreshold,\n      print: false\n    },\n    (err, results) => {\n      if (err) {\n        throw err\n      }\n\n      printResults(results)\n      dispatcher.destroy()\n      http2NativeClient.close()\n    }\n  )\n}\n\nif (isMainThread) {\n  main()\n} else {\n  module.exports = main\n}\n"
  },
  {
    "path": "benchmarks/benchmark-https.js",
    "content": "'use strict'\n\nconst https = require('node:https')\nconst os = require('node:os')\nconst path = require('node:path')\nconst { readFileSync } = require('node:fs')\nconst { Writable } = require('node:stream')\nconst { isMainThread } = require('node:worker_threads')\n\nconst { Pool, Client, fetch, Agent, setGlobalDispatcher } = require('..')\n\nconst ca = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'ca.pem'), 'utf8')\nconst servername = 'agent1'\n\nconst iterations = (parseInt(process.env.SAMPLES, 10) || 10) + 1\nconst errorThreshold = parseInt(process.env.ERROR_THRESHOLD, 10) || 3\nconst connections = parseInt(process.env.CONNECTIONS, 10) || 50\nconst pipelining = parseInt(process.env.PIPELINING, 10) || 10\nconst parallelRequests = parseInt(process.env.PARALLEL, 10) || 100\nconst headersTimeout = parseInt(process.env.HEADERS_TIMEOUT, 10) || 0\nconst bodyTimeout = parseInt(process.env.BODY_TIMEOUT, 10) || 0\nconst dest = {}\n\nif (process.env.PORT) {\n  dest.port = process.env.PORT\n  dest.url = `https://localhost:${process.env.PORT}`\n} else {\n  dest.url = 'https://localhost'\n  dest.socketPath = path.join(os.tmpdir(), 'undici.sock')\n}\n\nconst httpsBaseOptions = {\n  ca,\n  servername,\n  protocol: 'https:',\n  hostname: 'localhost',\n  method: 'GET',\n  path: '/',\n  query: {\n    frappucino: 'muffin',\n    goat: 'scone',\n    pond: 'moose',\n    foo: ['bar', 'baz', 'bal'],\n    bool: true,\n    numberKey: 256\n  },\n  ...dest\n}\n\nconst httpsNoKeepAliveOptions = {\n  ...httpsBaseOptions,\n  agent: new https.Agent({\n    keepAlive: false,\n    maxSockets: connections,\n    // rejectUnauthorized: false,\n    ca,\n    servername\n  })\n}\n\nconst httpsKeepAliveOptions = {\n  ...httpsBaseOptions,\n  agent: new https.Agent({\n    keepAlive: true,\n    maxSockets: connections,\n    // rejectUnauthorized: false,\n    ca,\n    servername\n  })\n}\n\nconst undiciOptions = {\n  path: '/',\n  method: 'GET',\n  headersTimeout,\n  bodyTimeout\n}\n\nconst Class = connections > 1 ? Pool : Client\nconst dispatcher = new Class(httpsBaseOptions.url, {\n  pipelining,\n  connections,\n  connect: {\n    // rejectUnauthorized: false,\n    ca,\n    servername\n  },\n  ...dest\n})\n\nsetGlobalDispatcher(new Agent({\n  pipelining,\n  connections,\n  connect: {\n    // rejectUnauthorized: false,\n    ca,\n    servername\n  }\n}))\n\nclass SimpleRequest {\n  constructor (resolve) {\n    this.dst = new Writable({\n      write (chunk, encoding, callback) {\n        callback()\n      }\n    }).on('finish', resolve)\n  }\n\n  onConnect (abort) { }\n\n  onHeaders (statusCode, headers, resume) {\n    this.dst.on('drain', resume)\n  }\n\n  onData (chunk) {\n    return this.dst.write(chunk)\n  }\n\n  onComplete () {\n    this.dst.end()\n  }\n\n  onError (err) {\n    throw err\n  }\n}\n\nfunction makeParallelRequests (cb) {\n  return Promise.all(Array.from(Array(parallelRequests)).map(() => new Promise(cb)))\n}\n\nfunction printResults (results) {\n  // Sort results by least performant first, then compare relative performances and also printing padding\n  let last\n\n  const rows = Object.entries(results)\n    // If any failed, put on the top of the list, otherwise order by mean, ascending\n    .sort((a, b) => (!a[1].success ? -1 : b[1].mean - a[1].mean))\n    .map(([name, result]) => {\n      if (!result.success) {\n        return {\n          Tests: name,\n          Samples: result.size,\n          Result: 'Errored',\n          Tolerance: 'N/A',\n          'Difference with Slowest': 'N/A'\n        }\n      }\n\n      // Calculate throughput and relative performance\n      const { size, mean, standardError } = result\n      const relative = last !== 0 ? (last / mean - 1) * 100 : 0\n\n      // Save the slowest for relative comparison\n      if (typeof last === 'undefined') {\n        last = mean\n      }\n\n      return {\n        Tests: name,\n        Samples: size,\n        Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,\n        Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,\n        'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'\n      }\n    })\n\n  return console.table(rows)\n}\n\nconst experiments = {\n  'https - no keepalive' () {\n    return makeParallelRequests(resolve => {\n      https.get(httpsNoKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'https - keepalive' () {\n    return makeParallelRequests(resolve => {\n      https.get(httpsKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'undici - pipeline' () {\n    return makeParallelRequests(resolve => {\n      dispatcher\n        .pipeline(undiciOptions, data => {\n          return data.body\n        })\n        .end()\n        .pipe(\n          new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        )\n        .on('finish', resolve)\n    })\n  },\n  'undici - request' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.request(undiciOptions).then(({ body }) => {\n        body\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'undici - stream' () {\n    return makeParallelRequests(resolve => {\n      return dispatcher\n        .stream(undiciOptions, () => {\n          return new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        })\n        .then(resolve)\n    })\n  },\n  'undici - dispatch' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.dispatch(undiciOptions, new SimpleRequest(resolve))\n    })\n  }\n}\n\nif (process.env.PORT) {\n  // fetch does not support the socket\n  experiments['undici - fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      fetch(dest.url, {}).then(res => {\n        res.body.pipeTo(new WritableStream({ write () { }, close () { resolve() } }))\n      }).catch(console.log)\n    })\n  }\n}\n\nasync function main () {\n  const { cronometro } = await import('cronometro')\n\n  cronometro(\n    experiments,\n    {\n      iterations,\n      errorThreshold,\n      print: false\n    },\n    (err, results) => {\n      if (err) {\n        throw err\n      }\n\n      printResults(results)\n      dispatcher.destroy()\n    }\n  )\n}\n\nif (isMainThread) {\n  main()\n} else {\n  module.exports = main\n}\n"
  },
  {
    "path": "benchmarks/benchmark.js",
    "content": "'use strict'\n\nconst http = require('node:http')\nconst os = require('node:os')\nconst path = require('node:path')\nconst { Writable } = require('node:stream')\nconst { isMainThread } = require('node:worker_threads')\n\nconst { Pool, Client, fetch, Agent, setGlobalDispatcher } = require('..')\n\nconst { makeParallelRequests, printResults } = require('./_util')\n\nlet nodeFetch\nconst axios = require('axios')\nlet superagent\nlet got\n\nconst { promisify } = require('node:util')\nconst request = promisify(require('request'))\n\nconst iterations = (parseInt(process.env.SAMPLES, 10) || 10) + 1\nconst errorThreshold = parseInt(process.env.ERROR_THRESHOLD, 10) || 3\nconst connections = parseInt(process.env.CONNECTIONS, 10) || 50\nconst pipelining = parseInt(process.env.PIPELINING, 10) || 10\nconst headersTimeout = parseInt(process.env.HEADERS_TIMEOUT, 10) || 0\nconst bodyTimeout = parseInt(process.env.BODY_TIMEOUT, 10) || 0\nconst dest = {}\n\nif (process.env.PORT) {\n  dest.port = process.env.PORT\n  dest.url = `http://localhost:${process.env.PORT}`\n} else {\n  dest.url = 'http://localhost'\n  dest.socketPath = path.join(os.tmpdir(), 'undici.sock')\n}\n\n/** @type {http.RequestOptions} */\nconst httpBaseOptions = {\n  protocol: 'http:',\n  hostname: 'localhost',\n  method: 'GET',\n  path: '/',\n  ...dest\n}\n\n/** @type {http.RequestOptions} */\nconst httpNoKeepAliveOptions = {\n  ...httpBaseOptions,\n  agent: new http.Agent({\n    keepAlive: false,\n    maxSockets: connections\n  })\n}\n\n/** @type {http.RequestOptions} */\nconst httpKeepAliveOptions = {\n  ...httpBaseOptions,\n  agent: new http.Agent({\n    keepAlive: true,\n    maxSockets: connections\n  })\n}\n\nconst axiosAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst fetchAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst gotAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst requestAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst superagentAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst undiciOptions = {\n  path: '/',\n  method: 'GET',\n  blocking: false,\n  reset: false,\n  headersTimeout,\n  bodyTimeout\n}\n\nconst Class = connections > 1 ? Pool : Client\nconst dispatcher = new Class(httpBaseOptions.url, {\n  pipelining,\n  connections,\n  ...dest\n})\n\nsetGlobalDispatcher(new Agent({\n  pipelining,\n  connections,\n  connect: {\n    rejectUnauthorized: false\n  }\n}))\n\nclass SimpleRequest {\n  constructor (resolve) {\n    this.dst = new Writable({\n      write (chunk, encoding, callback) {\n        callback()\n      }\n    }).on('finish', resolve)\n  }\n\n  onConnect (abort) { }\n\n  onHeaders (statusCode, headers, resume) {\n    this.dst.on('drain', resume)\n  }\n\n  onData (chunk) {\n    return this.dst.write(chunk)\n  }\n\n  onComplete () {\n    this.dst.end()\n  }\n\n  onError (err) {\n    throw err\n  }\n}\n\nconst experiments = {\n  'http - no keepalive' () {\n    return makeParallelRequests(resolve => {\n      http.get(httpNoKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'http - keepalive' () {\n    return makeParallelRequests(resolve => {\n      http.get(httpKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'undici - pipeline' () {\n    return makeParallelRequests(resolve => {\n      dispatcher\n        .pipeline(undiciOptions, ({ body }) => {\n          return body\n        })\n        .end()\n        .pipe(\n          new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        )\n        .on('finish', resolve)\n    })\n  },\n  'undici - request' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.request(undiciOptions).then(({ body }) => {\n        body\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'undici - stream' () {\n    return makeParallelRequests(resolve => {\n      return dispatcher\n        .stream(undiciOptions, () => {\n          return new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        })\n        .then(resolve)\n    })\n  },\n  'undici - dispatch' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.dispatch(undiciOptions, new SimpleRequest(resolve))\n    })\n  }\n}\n\nif (process.env.PORT) {\n  // fetch does not support the socket\n  experiments['undici - fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      fetch(dest.url).then(res => {\n        res.body.pipeTo(new WritableStream({ write () { }, close () { resolve() } }))\n      }).catch(console.log)\n    })\n  }\n\n  experiments['node-fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      nodeFetch(dest.url, { agent: fetchAgent }).then(res => {\n        res.body.pipe(new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        })).on('finish', resolve)\n      }).catch(console.log)\n    })\n  }\n\n  const axiosOptions = {\n    url: dest.url,\n    method: 'GET',\n    responseType: 'stream',\n    httpAgent: axiosAgent\n  }\n  experiments.axios = () => {\n    return makeParallelRequests(resolve => {\n      axios.request(axiosOptions).then(res => {\n        res.data.pipe(new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        })).on('finish', resolve)\n      }).catch(console.log)\n    })\n  }\n\n  const gotOptions = {\n    url: dest.url,\n    method: 'GET',\n    agent: {\n      http: gotAgent\n    },\n    // avoid body processing\n    isStream: true\n  }\n  experiments.got = () => {\n    return makeParallelRequests(resolve => {\n      got(gotOptions).pipe(new Writable({\n        write (chunk, encoding, callback) {\n          callback()\n        }\n      })).on('finish', resolve)\n    })\n  }\n\n  const requestOptions = {\n    url: dest.url,\n    method: 'GET',\n    agent: requestAgent,\n    // avoid body toString\n    encoding: null\n  }\n  experiments.request = () => {\n    return makeParallelRequests(resolve => {\n      request(requestOptions).then(() => {\n        // already body consumed\n        resolve()\n      }).catch(console.log)\n    })\n  }\n\n  experiments.superagent = () => {\n    return makeParallelRequests(resolve => {\n      superagent.get(dest.url).pipe(new Writable({\n        write (chunk, encoding, callback) {\n          callback()\n        }\n      })).on('finish', resolve)\n    })\n  }\n}\n\nasync function main () {\n  const { cronometro } = await import('cronometro')\n  const _nodeFetch = await import('node-fetch')\n  nodeFetch = _nodeFetch.default\n  const _got = await import('got')\n  got = _got.default\n  const _superagent = await import('superagent')\n  // https://github.com/ladjs/superagent/issues/1540#issue-561464561\n  superagent = _superagent.agent().use((req) => req.agent(superagentAgent))\n\n  cronometro(\n    experiments,\n    {\n      iterations,\n      errorThreshold,\n      print: false\n    },\n    (err, results) => {\n      if (err) {\n        throw err\n      }\n\n      printResults(results)\n      dispatcher.destroy()\n    }\n  )\n}\n\nif (isMainThread) {\n  main()\n} else {\n  module.exports = main\n}\n"
  },
  {
    "path": "benchmarks/cache/date.mjs",
    "content": "'use strict'\n\nimport { group, bench, run } from 'mitata'\nimport { parseHttpDate } from '../../lib/util/date.js'\n\nconst DATES = [\n  // IMF\n  'Sun, 06 Nov 1994 08:49:37 GMT',\n  'Thu, 18 Aug 1950 02:01:18 GMT',\n  'Wed, 11 Dec 2024 23:20:57 GMT',\n  'Wed, aa Dec 2024 23:20:57 GMT',\n  'aaa, 06 Dec 2024 23:20:57 GMT',\n  'Wed, 01 aaa 2024 23:20:57 GMT',\n  'Wed, 6 Dec 2024 23:20:07 GMT',\n  'Wed, 06 Dec 2024 3:20:07 GMT',\n  'Wed, 06 Dec 2024 23:1:07 GMT',\n  'Wed, 06 Dec 2024 23:01:7 GMT',\n  'Wed, 06 Dec aaaa 23:01:07 GMT',\n  'Wed, 06 Dec 2024 aa:01:07 GMT',\n  'Wed, 06 Dec 2024 23:aa:07 GMT',\n  'Wed, 06 Dec 2024 23:01:aa GMT',\n\n  // RFC850\n  'Sunday, 06-Nov-94 08:49:37 GMT',\n  'Thursday, 18-Aug-50 02:01:18 GMT',\n  'Wednesday, 11-Dec-24 23:20:57 GMT',\n  'Wednesday, aa Dec 2024 23:20:57 GMT',\n  'aaa, 06 Dec 2024 23:20:57 GMT',\n  'Wednesday, 01-aaa-24 23:20:57 GMT',\n  'Wednesday, 6-Dec-24 23:20:07 GMT',\n  'Wednesday, 06-Dec-24 3:20:07 GMT',\n  'Wednesday, 06-Dec-24 23:1:07 GMT',\n  'Wednesday, 06-Dec-24 23:01:7 GMT',\n  'Wednesday, 06 Dec-aa 23:01:07 GMT',\n  'Wednesday, 06-Dec-24 aa:01:07 GMT',\n  'Wednesday, 06-Dec-24 23:aa:07 GMT',\n  'Wednesday, 06-Dec-24 23:01:aa GMT',\n\n  // asctime()\n  'Sun Nov  6 08:49:37 1994',\n  'Thu Aug 18 02:01:18 1950',\n  'Wed Dec 11 23:20:57 2024',\n  'Wed Dec aa 23:20:57 2024',\n  'aaa Dec 06 23:20:57 2024',\n  'Wed aaa 01 23:20:57 2024',\n  'Wed Dec 6 23:20:07 2024',\n  'Wed Dec 06 3:20:07 2024',\n  'Wed Dec 06 23:1:07 2024',\n  'Wed Dec 06 23:01:7 2024',\n  'Wed 06 Dec 23:01:07 aaaa',\n  'Wed Dec 06 aa:01:07 2024',\n  'Wed Dec 06 23:aa:07 2024',\n  'Wed Dec 06 23:01:aa 2024'\n]\n\ngroup(() => {\n  bench('parseHttpDate', () => {\n    for (const date of DATES) {\n      parseHttpDate(date)\n    }\n  })\n\n  bench('new Date()', () => {\n    for (const date of DATES) {\n      // eslint-disable-next-line no-new\n      new Date(date)\n    }\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/cache/get-field-values.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { getFieldValues } from '../../lib/web/cache/util.js'\n\nconst values = [\n  '',\n  'foo',\n  'invälid',\n  'foo, ',\n  'foo, bar',\n  'foo, bar, baz',\n  'foo, bar, baz, ',\n  'foo, bar, baz, , '\n]\n\ngroup('getFieldValues', () => {\n  bench('getFieldValues', () => {\n    for (let i = 0; i < values.length; ++i) {\n      getFieldValues(values[i])\n    }\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/cookies/is-ctl-excluding-htab.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isCTLExcludingHtab } from '../../lib/web/cookies/util.js'\n\nconst valid = 'Space=Cat; Secure; HttpOnly; Max-Age=2'\nconst invalid = 'Space=Cat; Secure; HttpOnly; Max-Age=2\\x7F'\n\ngroup('isCTLExcludingHtab', () => {\n  bench(`valid: ${valid}`, () => {\n    return isCTLExcludingHtab(valid)\n  })\n\n  bench(`invalid: ${invalid}`, () => {\n    return isCTLExcludingHtab(invalid)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/cookies/to-imf-date.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { toIMFDate } from '../../lib/web/cookies/util.js'\n\nconst date = new Date()\n\ngroup('toIMFDate', () => {\n  bench(`toIMFDate: ${date}`, () => {\n    return toIMFDate(date)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/cookies/validate-cookie-name.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { validateCookieName } from '../../lib/web/cookies/util.js'\n\nconst valid = 'Cat'\n\ngroup('validateCookieName', () => {\n  bench(`valid: ${valid}`, () => {\n    return validateCookieName(valid)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/cookies/validate-cookie-value.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { validateCookieValue } from '../../lib/web/cookies/util.js'\n\nconst valid = 'Cat'\nconst wrappedValid = `\"${valid}\"`\n\ngroup('validateCookieValue', () => {\n  bench(`valid: ${valid}`, () => {\n    return validateCookieValue(valid)\n  })\n  bench(`valid: ${wrappedValid}`, () => {\n    return validateCookieValue(wrappedValid)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/is-blob-like.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isBlobLike } from '../../lib/core/util.js'\n\nconst buffer = Buffer.alloc(1)\n\nconst blob = new Blob(['asd'], {\n  type: 'application/json'\n})\n\nconst file = new File(['asd'], 'file.txt', {\n  type: 'text/plain'\n})\n\nconst blobLikeStream = {\n  [Symbol.toStringTag]: 'Blob',\n  stream: () => {}\n}\n\nconst fileLikeStream = {\n  stream: () => {},\n  [Symbol.toStringTag]: 'File'\n}\n\nconst blobLikeArrayBuffer = {\n  [Symbol.toStringTag]: 'Blob',\n  arrayBuffer: () => {}\n}\n\nconst fileLikeArrayBuffer = {\n  [Symbol.toStringTag]: 'File',\n  arrayBuffer: () => {}\n}\n\ngroup('isBlobLike', () => {\n  bench('blob', () => {\n    return isBlobLike(blob)\n  })\n  bench('file', () => {\n    return isBlobLike(file)\n  })\n  bench('blobLikeStream', () => {\n    return isBlobLike(blobLikeStream)\n  })\n  bench('fileLikeStream', () => {\n    return isBlobLike(fileLikeStream)\n  })\n  bench('fileLikeArrayBuffer', () => {\n    return isBlobLike(fileLikeArrayBuffer)\n  })\n  bench('blobLikeArrayBuffer', () => {\n    return isBlobLike(blobLikeArrayBuffer)\n  })\n  bench('buffer', () => {\n    return isBlobLike(buffer)\n  })\n  bench('null', () => {\n    return isBlobLike(null)\n  })\n  bench('string', () => {\n    return isBlobLike('invalid')\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/is-valid-header-char.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isValidHeaderChar } from '../../lib/core/util.js'\n\nconst html = 'text/html'\nconst json = 'application/json; charset=UTF-8'\n\nconst headerCharRegex = /[^\\t\\x20-\\x7e\\x80-\\xff]/\n\n/**\n * @param {string} characters\n */\nfunction charCodeAtApproach (characters) {\n  // Validate if characters is a valid field-vchar.\n  //  field-value    = *( field-content / obs-fold )\n  //  field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]\n  //  field-vchar    = VCHAR / obs-text\n  for (let i = 0; i < characters.length; ++i) {\n    const code = characters.charCodeAt(i)\n    // not \\x20-\\x7e, \\t and \\x80-\\xff\n    if ((code < 0x20 && code !== 0x09) || code === 0x7f || code > 0xff) {\n      return false\n    }\n  }\n  return true\n}\n\ngroup(`isValidHeaderChar# ${html}`, () => {\n  bench('regexp.test', () => {\n    return !headerCharRegex.test(html)\n  })\n  bench('regexp.exec', () => {\n    return headerCharRegex.exec(html) === null\n  })\n  bench('charCodeAt', () => {\n    return charCodeAtApproach(html)\n  })\n  bench('isValidHeaderChar', () => {\n    return isValidHeaderChar(html)\n  })\n})\n\ngroup(`isValidHeaderChar# ${json}`, () => {\n  bench('regexp.test', () => {\n    return !headerCharRegex.test(json)\n  })\n  bench('regexp.exec', () => {\n    return headerCharRegex.exec(json) === null\n  })\n  bench('charCodeAt', () => {\n    return charCodeAtApproach(json)\n  })\n  bench('isValidHeaderChar', () => {\n    return isValidHeaderChar(json)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/is-valid-port.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isValidPort } from '../../lib/core/util.js'\n\nconst string = '1234'\nconst number = 1234\n\ngroup('isValidPort', () => {\n  bench('string', () => {\n    return isValidPort(string)\n  })\n  bench('number', () => {\n    return isValidPort(number)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/parse-headers.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { parseHeaders } from '../../lib/core/util.js'\n\nconst target = [\n  {\n    'Content-Type': 'application/json',\n    Date: 'Wed, 01 Nov 2023 00:00:00 GMT',\n    'Powered-By': 'NodeJS',\n    'Content-Encoding': 'gzip',\n    'Set-Cookie': '__Secure-ID=123; Secure; Domain=example.com',\n    'Content-Length': '150',\n    Vary: 'Accept-Encoding, Accept, X-Requested-With'\n  },\n  {\n    'Content-Type': 'text/html; charset=UTF-8',\n    'Content-Length': '1234',\n    Date: 'Wed, 06 Dec 2023 12:47:57 GMT',\n    Server: 'Bing'\n  },\n  {\n    'Content-Type': 'image/jpeg',\n    'Content-Length': '56789',\n    Date: 'Wed, 06 Dec 2023 12:48:12 GMT',\n    Server: 'Bing',\n    ETag: '\"a1b2c3d4e5f6g7h8i9j0\"'\n  },\n  {\n    Cookie: 'session_id=1234567890abcdef',\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',\n    Host: 'www.bing.com',\n    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n    'Accept-Language': 'en-US,en;q=0.5',\n    'Accept-Encoding': 'gzip, deflate, br'\n  },\n  {\n    Location: 'https://www.bing.com/search?q=bing',\n    Status: '302 Found',\n    Date: 'Wed, 06 Dec 2023 12:48:27 GMT',\n    Server: 'Bing',\n    'Content-Type': 'text/html; charset=UTF-8',\n    'Content-Length': '0'\n  },\n  {\n    'Content-Type':\n      'multipart/form-data; boundary=----WebKitFormBoundary1234567890',\n    'Content-Length': '98765',\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',\n    Host: 'www.bing.com',\n    Accept: '*/*',\n    'Accept-Language': 'en-US,en;q=0.5',\n    'Accept-Encoding': 'gzip, deflate, br'\n  },\n  {\n    'Content-Type': 'application/json; charset=UTF-8',\n    'Content-Length': '2345',\n    Date: 'Wed, 06 Dec 2023 12:48:42 GMT',\n    Server: 'Bing',\n    Status: '200 OK',\n    'Cache-Control': 'no-cache, no-store, must-revalidate'\n  },\n  {\n    Host: 'www.example.com',\n    Connection: 'keep-alive',\n    Accept: 'text/html, application/xhtml+xml, application/xml;q=0.9,;q=0.8',\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'\n  }\n]\n\nconst headers = Array.from(target, (x) =>\n  Object.entries(x)\n    .flat()\n    .map((c) => Buffer.from(c))\n)\n\nconst headersIrregular = Array.from(\n  target,\n  (x) => Object.entries(x)\n    .flat()\n    .map((c) => Buffer.from(c.toUpperCase()))\n)\n\n// avoid JIT bias\nbench('noop', () => {})\nbench('noop', () => {})\nbench('noop', () => {})\nbench('noop', () => {})\nbench('noop', () => {})\nbench('noop', () => {})\n\ngroup('parseHeaders', () => {\n  bench('parseHeaders', () => {\n    for (let i = 0; i < headers.length; ++i) {\n      parseHeaders(headers[i])\n    }\n  })\n  bench('parseHeaders (irregular)', () => {\n    for (let i = 0; i < headersIrregular.length; ++i) {\n      parseHeaders(headersIrregular[i])\n    }\n  })\n})\n\nawait new Promise((resolve) => setTimeout(resolve, 7000))\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/parse-raw-headers.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { parseRawHeaders } from '../../lib/core/util.js'\n\nconst rawHeadersMixed = ['key', 'value', Buffer.from('key'), Buffer.from('value')]\nconst rawHeadersOnlyStrings = ['key', 'value', 'key', 'value']\nconst rawHeadersOnlyBuffers = [Buffer.from('key'), Buffer.from('value'), Buffer.from('key'), Buffer.from('value')]\nconst rawHeadersContent = ['content-length', 'value', 'content-disposition', 'form-data; name=\"fieldName\"']\n\ngroup('parseRawHeaders', () => {\n  bench('only strings', () => {\n    parseRawHeaders(rawHeadersOnlyStrings)\n  })\n  bench('only buffers', () => {\n    parseRawHeaders(rawHeadersOnlyBuffers)\n  })\n  bench('mixed', () => {\n    parseRawHeaders(rawHeadersMixed)\n  })\n  bench('content-disposition special case', () => {\n    parseRawHeaders(rawHeadersContent)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/request-instantiation.mjs",
    "content": "import { bench, run } from 'mitata'\n\nimport Request from '../../lib/core/request.js'\nimport DecoratorHandler from '../../lib/handler/decorator-handler.js'\n\nconst handler = new DecoratorHandler({})\n\nbench('new Request()', () => {\n  return new Request('https://localhost', { path: '/', method: 'get', body: null }, handler)\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/core/tree.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { tree } from '../../lib/core/tree.js'\n\nconst contentLength = Buffer.from('Content-Length')\nconst contentLengthUpperCase = Buffer.from('Content-Length'.toUpperCase())\nconst contentLengthLowerCase = Buffer.from('Content-Length'.toLowerCase())\n\ngroup('tree.search', () => {\n  bench('content-length', () => {\n    tree.lookup(contentLengthLowerCase)\n  })\n  bench('CONTENT-LENGTH', () => {\n    tree.lookup(contentLengthUpperCase)\n  })\n  bench('Content-Length', () => {\n    tree.lookup(contentLength)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/body-arraybuffer.mjs",
    "content": "import { group, bench, run } from 'mitata'\nimport { Response } from '../../lib/web/fetch/response.js'\n\nconst settings = {\n  small: 2 << 8,\n  middle: 2 << 12,\n  long: 2 << 16\n}\n\nfor (const [name, length] of Object.entries(settings)) {\n  const buffer = Buffer.allocUnsafe(length).map(() => (Math.random() * 100) | 0)\n  group(`${name} (length ${length})`, () => {\n    bench('Response#arrayBuffer', async () => {\n      return await new Response(buffer).arrayBuffer()\n    })\n\n    // for comparison\n    bench('Response#text', async () => {\n      return await new Response(buffer).text()\n    })\n  })\n}\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/bytes-match.mjs",
    "content": "import { createHash } from 'node:crypto'\nimport { bench, run } from 'mitata'\nimport { bytesMatch } from '../../lib/web/fetch/util.js'\n\nconst body = Buffer.from('Hello world!')\nconst validSha256Base64 = `sha256-${createHash('sha256').update(body).digest('base64')}`\nconst invalidSha256Base64 = `sha256-${createHash('sha256').update(body).digest('base64')}`\nconst validSha256Base64Url = `sha256-${createHash('sha256').update(body).digest('base64url')}`\nconst invalidSha256Base64Url = `sha256-${createHash('sha256').update(body).digest('base64url')}`\n\nbench('bytesMatch valid sha256 and base64', () => {\n  bytesMatch(body, validSha256Base64)\n})\nbench('bytesMatch invalid sha256 and base64', () => {\n  bytesMatch(body, invalidSha256Base64)\n})\nbench('bytesMatch valid sha256 and base64url', () => {\n  bytesMatch(body, validSha256Base64Url)\n})\nbench('bytesMatch invalid sha256 and base64url', () => {\n  bytesMatch(body, invalidSha256Base64Url)\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/headers-length32.mjs",
    "content": "import { bench, run } from 'mitata'\nimport { Headers, getHeadersList } from '../../lib/web/fetch/headers.js'\n\nconst headers = new Headers(\n  [\n    'Origin-Agent-Cluster',\n    'RTT',\n    'Accept-CH-Lifetime',\n    'X-Frame-Options',\n    'Sec-CH-UA-Platform-Version',\n    'Digest',\n    'Cache-Control',\n    'Sec-CH-UA-Platform',\n    'If-Range',\n    'SourceMap',\n    'Strict-Transport-Security',\n    'Want-Digest',\n    'Cross-Origin-Resource-Policy',\n    'Width',\n    'Accept-CH',\n    'Via',\n    'Set-Cookie',\n    'Server',\n    'Sec-Fetch-Dest',\n    'Sec-CH-UA-Model',\n    'Access-Control-Request-Method',\n    'Access-Control-Request-Headers',\n    'Date',\n    'Expires',\n    'DNT',\n    'Proxy-Authorization',\n    'Alt-Svc',\n    'Alt-Used',\n    'ETag',\n    'Sec-Fetch-User',\n    'Sec-CH-UA-Full-Version-List',\n    'Referrer-Policy'\n  ].map((v) => [v, ''])\n)\n\nconst headersList = getHeadersList(headers)\n\nconst kHeadersSortedMap = Reflect.ownKeys(headersList).find(\n  (c) => String(c) === 'Symbol(headers map sorted)'\n)\n\nbench('Headers@@iterator', () => {\n  headersList[kHeadersSortedMap] = null\n  return [...headers]\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/headers.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { Headers, getHeadersList } from '../../lib/web/fetch/headers.js'\n\nconst characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\nconst charactersLength = characters.length\n\nfunction generateAsciiString (length) {\n  let result = ''\n  for (let i = 0; i < length; ++i) {\n    result += characters[Math.floor(Math.random() * charactersLength)]\n  }\n  return result\n}\n\nconst settings = {\n  'fast-path (tiny array)': 4,\n  'fast-path (small array)': 8,\n  'fast-path (middle array)': 16,\n  'fast-path': 32,\n  'slow-path': 64\n}\n\nfor (const [name, length] of Object.entries(settings)) {\n  const headers = new Headers(\n    Array.from(Array(length), () => [generateAsciiString(12), ''])\n  )\n\n  const headersSorted = new Headers(headers)\n\n  const headersList = getHeadersList(headers)\n\n  const headersListSorted = getHeadersList(headersSorted)\n\n  const kHeadersSortedMap = Reflect.ownKeys(headersList).find(\n    (c) => String(c) === 'Symbol(headers map sorted)'\n  )\n\n  group(`length ${length} #${name}`, () => {\n    bench('Headers@@iterator', () => {\n      // prevention of memoization of results\n      headersList[kHeadersSortedMap] = null\n      return [...headers]\n    })\n\n    bench('Headers@@iterator (sorted)', () => {\n      // prevention of memoization of results\n      headersListSorted[kHeadersSortedMap] = null\n      return [...headersSorted]\n    })\n  })\n}\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/is-valid-encoded-url.mjs",
    "content": "import { bench, run } from 'mitata'\nimport { isValidEncodedURL } from '../../lib/web/fetch/util.js'\n\nconst validUrl = 'https://example.com'\nconst invalidUrl = 'https://example.com\\x00'\n\nbench('isValidEncodedURL valid', () => {\n  isValidEncodedURL(validUrl)\n})\nbench('isValidEncodedURL invalid', () => {\n  isValidEncodedURL(invalidUrl)\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/is-valid-header-value.mjs",
    "content": "import { bench, run } from 'mitata'\nimport { isValidHeaderValue } from '../../lib/web/fetch/util.js'\n\nconst valid = 'valid123'\nconst invalidNUL = 'invalid\\x00'\nconst invalidCR = 'invalid\\r'\nconst invalidLF = 'invalid\\n'\nconst invalidTrailingTab = 'invalid\\t'\nconst invalidLeadingTab = '\\tinvalid'\nconst invalidTrailingSpace = 'invalid '\nconst invalidLeadingSpace = ' invalid'\n\nbench('isValidHeaderValue valid', () => {\n  isValidHeaderValue(valid)\n})\nbench('isValidHeaderValue invalid containing NUL', () => {\n  isValidHeaderValue(invalidNUL)\n})\nbench('isValidHeaderValue invalid containing CR', () => {\n  isValidHeaderValue(invalidCR)\n})\nbench('isValidHeaderValue invalid containing LF', () => {\n  isValidHeaderValue(invalidLF)\n})\nbench('isValidHeaderValue invalid trailing TAB', () => {\n  isValidHeaderValue(invalidTrailingTab)\n})\nbench('isValidHeaderValue invalid leading TAB', () => {\n  isValidHeaderValue(invalidLeadingTab)\n})\nbench('isValidHeaderValue invalid trailing SPACE', () => {\n  isValidHeaderValue(invalidTrailingSpace)\n})\nbench('isValidHeaderValue invalid leading SPACE', () => {\n  isValidHeaderValue(invalidLeadingSpace)\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/isomorphic-encode.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isomorphicEncode } from '../../lib/web/fetch/util.js'\n\nconst characters =\n  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\nconst charactersLength = characters.length\n\nfunction generateAsciiString (length) {\n  let result = ''\n  for (let i = 0; i < length; ++i) {\n    result += characters[Math.floor(Math.random() * charactersLength)]\n  }\n  return result\n}\n\nconst invalidIsomorphicEncodeValueRegex = /[^\\x00-\\xFF]/ // eslint-disable-line\n\nfunction isomorphicEncode1 (input) {\n  for (let i = 0; i < input.length; i++) {\n    if (input.charCodeAt(i) > 0xff) {\n      throw new TypeError('Unreachable')\n    }\n  }\n  return input\n}\n\nfunction isomorphicEncode2 (input) {\n  if (invalidIsomorphicEncodeValueRegex.test(input)) {\n    throw new TypeError('Unreachable')\n  }\n  return input\n}\n\nconst settings = {\n  small: 10,\n  middle: 30,\n  long: 70\n}\n\nfor (const [runName, length] of Object.entries(settings)) {\n  const value = generateAsciiString(length);\n\n  [\n    { name: `${runName} (valid)`, value },\n    {\n      name: `${runName} (invalid)`,\n      value: `${value.slice(0, -1)}${String.fromCharCode(0xff + 1)}`\n    }\n  ].forEach(({ name, value }) => {\n    group(name, () => {\n      [\n        {\n          name: 'original',\n          fn: isomorphicEncode\n        },\n        {\n          name: 'String#charCodeAt',\n          fn: isomorphicEncode1\n        },\n        {\n          name: 'RegExp#test',\n          fn: isomorphicEncode2\n        }\n      ].forEach(({ name, fn }) => {\n        bench(name, () => {\n          try {\n            return fn(value)\n          } catch (err) {}\n        })\n      })\n    })\n  })\n}\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/request-creation.mjs",
    "content": "import { bench, run } from 'mitata'\nimport { Request } from '../../lib/web/fetch/request.js'\n\nconst input = 'https://example.com/post'\n\nbench('new Request(input)', () => new Request(input, undefined))\n\nawait run()\n"
  },
  {
    "path": "benchmarks/fetch/url-has-https-scheme.mjs",
    "content": "import { bench, run } from 'mitata'\nimport { urlHasHttpsScheme } from '../../lib/web/fetch/util.js'\n\nconst httpString = 'http://example.com'\nconst httpObject = { protocol: 'http:' }\nconst httpsString = 'https://example.com'\nconst httpsObject = { protocol: 'https:' }\n\nbench('urlHasHttpsScheme \"http:\" String', () => {\n  urlHasHttpsScheme(httpString)\n})\nbench('urlHasHttpsScheme \"https:\" String', () => {\n  urlHasHttpsScheme(httpsString)\n})\nbench('urlHasHttpsScheme \"http:\" Object', () => {\n  urlHasHttpsScheme(httpObject)\n})\nbench('urlHasHttpsScheme \"https:\" Object', () => {\n  urlHasHttpsScheme(httpsObject)\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/package.json",
    "content": "{\n  \"name\": \"benchmarks\",\n  \"scripts\": {\n    \"bench\": \"PORT=3042 concurrently -k -s first npm:bench:server npm:bench:run\",\n    \"bench:h2\": \"PORT=3052 concurrently -k -s first npm:bench:server:h2 npm:bench:run:h2\",\n    \"bench-post\": \"PORT=3042 concurrently -k -s first npm:bench:server npm:bench-post:run\",\n    \"bench:server\": \"node ./server.js\",\n    \"bench:server:h2\": \"node ./server-http2.js\",\n    \"prebench:run\": \"node ./wait.js\",\n    \"bench:run\": \"SAMPLES=100 CONNECTIONS=50 node ./benchmark.js\",\n    \"bench:run:h2\": \"SAMPLES=100 CONNECTIONS=50 node ./benchmark-http2.js\",\n    \"prebench-post:run\": \"node ./wait.js\",\n    \"bench-post:run\": \"SAMPLES=100 CONNECTIONS=50 node ./post-benchmark.js\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^1.6.7\",\n    \"concurrently\": \"^9.0.0\",\n    \"cronometro\": \"^5.3.0\",\n    \"got\": \"^14.2.0\",\n    \"mitata\": \"^1.0.4\",\n    \"node-fetch\": \"^3.3.2\",\n    \"request\": \"^2.88.2\",\n    \"superagent\": \"^10.0.0\",\n    \"tinybench\": \"^5.0.0\",\n    \"uWebSockets.js\": \"uNetworking/uWebSockets.js#v20.58.0\",\n    \"wait-on\": \"^9.0.1\"\n  }\n}\n"
  },
  {
    "path": "benchmarks/post-benchmark.js",
    "content": "'use strict'\n\nconst http = require('node:http')\nconst os = require('node:os')\nconst path = require('node:path')\nconst { Writable, Readable, pipeline } = require('node:stream')\nconst { isMainThread } = require('node:worker_threads')\n\nconst { Pool, Client, fetch, Agent, setGlobalDispatcher } = require('..')\n\nconst { makeParallelRequests, printResults } = require('./_util')\n\nlet nodeFetch\nconst axios = require('axios')\nlet superagent\nlet got\n\nconst { promisify } = require('node:util')\nconst request = promisify(require('request'))\n\nconst iterations = (parseInt(process.env.SAMPLES, 10) || 10) + 1\nconst errorThreshold = parseInt(process.env.ERROR_THRESHOLD, 10) || 3\nconst connections = parseInt(process.env.CONNECTIONS, 10) || 50\nconst pipelining = parseInt(process.env.PIPELINING, 10) || 10\nconst headersTimeout = parseInt(process.env.HEADERS_TIMEOUT, 10) || 0\nconst bodyTimeout = parseInt(process.env.BODY_TIMEOUT, 10) || 0\nconst dest = {}\n\nconst data = '_'.repeat(128 * 1024)\nconst dataLength = `${Buffer.byteLength(data)}`\n\nif (process.env.PORT) {\n  dest.port = process.env.PORT\n  dest.url = `http://localhost:${process.env.PORT}`\n} else {\n  dest.url = 'http://localhost'\n  dest.socketPath = path.join(os.tmpdir(), 'undici.sock')\n}\n\nconst headers = {\n  'Content-Type': 'text/plain; charset=UTF-8',\n  'Content-Length': dataLength\n}\n\n/** @type {http.RequestOptions} */\nconst httpBaseOptions = {\n  protocol: 'http:',\n  hostname: 'localhost',\n  method: 'POST',\n  path: '/',\n  headers,\n  ...dest\n}\n\n/** @type {http.RequestOptions} */\nconst httpNoKeepAliveOptions = {\n  ...httpBaseOptions,\n  agent: new http.Agent({\n    keepAlive: false,\n    maxSockets: connections\n  })\n}\n\n/** @type {http.RequestOptions} */\nconst httpKeepAliveOptions = {\n  ...httpBaseOptions,\n  agent: new http.Agent({\n    keepAlive: true,\n    maxSockets: connections\n  })\n}\n\nconst axiosAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst fetchAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst gotAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst requestAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\nconst superagentAgent = new http.Agent({\n  keepAlive: true,\n  maxSockets: connections\n})\n\n/** @type {import(\"..\").Dispatcher.DispatchOptions} */\nconst undiciOptions = {\n  path: '/',\n  method: 'POST',\n  headersTimeout,\n  bodyTimeout,\n  body: data,\n  headers\n}\n\nconst Class = connections > 1 ? Pool : Client\nconst dispatcher = new Class(httpBaseOptions.url, {\n  pipelining,\n  connections,\n  ...dest\n})\n\nsetGlobalDispatcher(new Agent({\n  pipelining,\n  connections,\n  connect: {\n    rejectUnauthorized: false\n  }\n}))\n\nclass SimpleRequest {\n  constructor (resolve) {\n    this.dst = new Writable({\n      write (chunk, encoding, callback) {\n        callback()\n      }\n    }).on('finish', resolve)\n  }\n\n  onConnect (abort) { }\n\n  onHeaders (statusCode, headers, resume) {\n    this.dst.on('drain', resume)\n  }\n\n  onData (chunk) {\n    return this.dst.write(chunk)\n  }\n\n  onComplete () {\n    this.dst.end()\n  }\n\n  onError (err) {\n    throw err\n  }\n}\n\nconst experiments = {\n  'http - no keepalive' () {\n    return makeParallelRequests(resolve => {\n      const request = http.request(httpNoKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n      request.end(data)\n    })\n  },\n  'http - keepalive' () {\n    return makeParallelRequests(resolve => {\n      const request = http.request(httpKeepAliveOptions, res => {\n        res\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n      request.end(data)\n    })\n  },\n  'undici - pipeline' () {\n    return makeParallelRequests(resolve => {\n      pipeline(\n        new Readable({\n          read () {\n            this.push(data)\n            this.push(null)\n          }\n        }),\n        dispatcher.pipeline(undiciOptions, ({ body }) => {\n          return body\n        }),\n        new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        }),\n        (err) => {\n          if (err != null) {\n            console.log(err)\n          }\n          resolve()\n        }\n      )\n    })\n  },\n  'undici - request' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.request(undiciOptions).then(({ body }) => {\n        body\n          .pipe(\n            new Writable({\n              write (chunk, encoding, callback) {\n                callback()\n              }\n            })\n          )\n          .on('finish', resolve)\n      })\n    })\n  },\n  'undici - stream' () {\n    return makeParallelRequests(resolve => {\n      return dispatcher\n        .stream(undiciOptions, () => {\n          return new Writable({\n            write (chunk, encoding, callback) {\n              callback()\n            }\n          })\n        })\n        .then(resolve)\n    })\n  },\n  'undici - dispatch' () {\n    return makeParallelRequests(resolve => {\n      dispatcher.dispatch(undiciOptions, new SimpleRequest(resolve))\n    })\n  }\n}\n\nif (process.env.PORT) {\n  /** @type {RequestInit} */\n  const fetchOptions = {\n    method: 'POST',\n    body: data,\n    headers\n  }\n  // fetch does not support the socket\n  experiments['undici - fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      fetch(dest.url, fetchOptions).then(res => {\n        res.body.pipeTo(new WritableStream({ write () { }, close () { resolve() } }))\n      }).catch(console.log)\n    })\n  }\n\n  const nodeFetchOptions = {\n    ...fetchOptions,\n    agent: fetchAgent\n  }\n  experiments['node-fetch'] = () => {\n    return makeParallelRequests(resolve => {\n      nodeFetch(dest.url, nodeFetchOptions).then(res => {\n        res.body.pipe(new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        })).on('finish', resolve)\n      }).catch(console.log)\n    })\n  }\n\n  const axiosOptions = {\n    url: dest.url,\n    method: 'POST',\n    headers,\n    responseType: 'stream',\n    httpAgent: axiosAgent,\n    data\n  }\n  experiments.axios = () => {\n    return makeParallelRequests(resolve => {\n      axios.request(axiosOptions).then(res => {\n        res.data.pipe(new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        })).on('finish', resolve)\n      }).catch(console.log)\n    })\n  }\n\n  const gotOptions = {\n    url: dest.url,\n    method: 'POST',\n    headers,\n    agent: {\n      http: gotAgent\n    },\n    // avoid body processing\n    isStream: true,\n    body: data\n  }\n  experiments.got = () => {\n    return makeParallelRequests(resolve => {\n      got(gotOptions).pipe(new Writable({\n        write (chunk, encoding, callback) {\n          callback()\n        }\n      })).on('finish', resolve)\n    })\n  }\n\n  const requestOptions = {\n    url: dest.url,\n    method: 'POST',\n    headers,\n    agent: requestAgent,\n    body: data,\n    // avoid body toString\n    encoding: null\n  }\n  experiments.request = () => {\n    return makeParallelRequests(resolve => {\n      request(requestOptions).then(() => {\n        // already body consumed\n        resolve()\n      }).catch(console.log)\n    })\n  }\n\n  experiments.superagent = () => {\n    return makeParallelRequests(resolve => {\n      superagent\n        .post(dest.url)\n        .send(data)\n        .set('Content-Type', 'text/plain; charset=UTF-8')\n        .set('Content-Length', dataLength)\n        .pipe(new Writable({\n          write (chunk, encoding, callback) {\n            callback()\n          }\n        })).on('finish', resolve)\n    })\n  }\n}\n\nasync function main () {\n  const { cronometro } = await import('cronometro')\n  const _nodeFetch = await import('node-fetch')\n  nodeFetch = _nodeFetch.default\n  const _got = await import('got')\n  got = _got.default\n  const _superagent = await import('superagent')\n  // https://github.com/ladjs/superagent/issues/1540#issue-561464561\n  superagent = _superagent.agent().use((req) => req.agent(superagentAgent))\n\n  cronometro(\n    experiments,\n    {\n      iterations,\n      errorThreshold,\n      print: false\n    },\n    (err, results) => {\n      if (err) {\n        throw err\n      }\n\n      printResults(results)\n      dispatcher.destroy()\n    }\n  )\n}\n\nif (isMainThread) {\n  main()\n} else {\n  module.exports = main\n}\n"
  },
  {
    "path": "benchmarks/server-http2.js",
    "content": "'use strict'\n\nconst { unlinkSync, readFileSync } = require('node:fs')\nconst { createSecureServer } = require('node:http2')\nconst os = require('node:os')\nconst path = require('node:path')\nconst cluster = require('node:cluster')\n\nconst key = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'key.pem'), 'utf8')\nconst cert = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'cert.pem'), 'utf8')\n\nconst socketPath = path.join(os.tmpdir(), 'undici.sock')\n\nconst port = process.env.PORT || socketPath\nconst timeout = parseInt(process.env.TIMEOUT, 10) || 1\nconst workers = parseInt(process.env.WORKERS) || os.cpus().length\n\nconst sessionTimeout = 600e3 // 10 minutes\n\nif (cluster.isPrimary) {\n  try {\n    unlinkSync(socketPath)\n  } catch (_) {\n    // Do nothing if the socket does not exist\n  }\n\n  for (let i = 0; i < workers; i++) {\n    cluster.fork()\n  }\n} else {\n  const buf = Buffer.alloc(64 * 1024, '_')\n  const server = createSecureServer(\n    {\n      key,\n      cert,\n      allowHTTP1: true,\n      sessionTimeout\n    }\n  )\n\n  server.on('stream', (stream) => {\n    setTimeout(() => {\n      stream.setEncoding('utf-8').end(buf)\n    }, timeout)\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      ':status': 200\n    })\n  })\n\n  server.keepAliveTimeout = 600e3\n\n  server.listen(port)\n}\n"
  },
  {
    "path": "benchmarks/server-https.js",
    "content": "'use strict'\n\nconst { unlinkSync, readFileSync } = require('node:fs')\nconst { createServer } = require('node:https')\nconst os = require('node:os')\nconst path = require('node:path')\nconst cluster = require('node:cluster')\n\nconst key = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'key.pem'), 'utf8')\nconst cert = readFileSync(path.join(__dirname, '..', 'test', 'fixtures', 'cert.pem'), 'utf8')\n\nconst socketPath = path.join(os.tmpdir(), 'undici.sock')\n\nconst port = process.env.PORT || socketPath\nconst timeout = parseInt(process.env.TIMEOUT, 10) || 1\nconst workers = parseInt(process.env.WORKERS) || os.cpus().length\n\nif (cluster.isPrimary) {\n  try {\n    unlinkSync(socketPath)\n  } catch (_) {\n    // Do nothing if the socket does not exist\n  }\n\n  for (let i = 0; i < workers; i++) {\n    cluster.fork()\n  }\n} else {\n  const buf = Buffer.alloc(64 * 1024, '_')\n  const server = createServer({\n    key,\n    cert,\n    keepAliveTimeout: 600e3\n  }, (req, res) => {\n    setTimeout(() => {\n      res.end(buf)\n    }, timeout)\n  })\n\n  server.listen(port)\n}\n"
  },
  {
    "path": "benchmarks/server.js",
    "content": "'use strict'\n\nconst { unlinkSync } = require('node:fs')\nconst { createServer } = require('node:http')\nconst os = require('node:os')\nconst path = require('node:path')\nconst cluster = require('node:cluster')\n\nconst socketPath = path.join(os.tmpdir(), 'undici.sock')\n\nconst port = process.env.PORT || socketPath\nconst timeout = parseInt(process.env.TIMEOUT, 10) || 1\nconst workers = parseInt(process.env.WORKERS) || os.cpus().length\n\nif (cluster.isPrimary) {\n  try {\n    unlinkSync(socketPath)\n  } catch (_) {\n    // Do nothing if the socket does not exist\n  }\n\n  for (let i = 0; i < workers; i++) {\n    cluster.fork()\n  }\n} else {\n  const buf = Buffer.alloc(64 * 1024, '_')\n\n  const headers = {\n    'Content-Length': `${buf.byteLength}`,\n    'Content-Type': 'text/plain; charset=UTF-8'\n  }\n  let i = 0\n  const server = createServer((_req, res) => {\n    i++\n    setTimeout(() => {\n      res.writeHead(200, headers)\n      res.end(buf)\n    }, timeout)\n  }).listen(port)\n  server.keepAliveTimeout = 600e3\n  setInterval(() => {\n    console.log(`Worker ${process.pid} processed ${i} requests`)\n  }, 5000)\n}\n"
  },
  {
    "path": "benchmarks/timers/compare-timer-getters.mjs",
    "content": "import { bench, group, run } from 'mitata'\n\ngroup('timers', () => {\n  bench('Date.now()', () => {\n    Date.now()\n  })\n  bench('performance.now()', () => {\n    performance.now()\n  })\n  bench('Math.trunc(performance.now())', () => {\n    Math.trunc(performance.now())\n  })\n  bench('process.uptime()', () => {\n    process.uptime()\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/utils/date.mjs",
    "content": "import { Bench } from 'tinybench'\n\nimport { parseHttpDate } from '../../lib/util/date.js'\n\nconst asctime = 'Sun Nov  6 08:49:37 1994'\nconst rfc850 = 'Sunday, 06-Nov-94 08:49:37 GMT'\nconst imf = 'Sun, 06 Nov 1994 08:49:37 GMT'\n\nconsole.assert(parseHttpDate(asctime) instanceof Date, 'asctime should return a Date')\nconsole.assert(parseHttpDate(rfc850) instanceof Date, 'rfc850 should return a Date')\nconsole.assert(parseHttpDate(imf) instanceof Date, 'imf should return a Date')\n\nconst bench = new Bench({ name: 'parseHttpDate' })\n\nbench\n  .add('asctime', () => {\n    parseHttpDate(asctime)\n  })\n  .add('rfc850', () => {\n    parseHttpDate(rfc850)\n  })\n  .add('imf', () => {\n    parseHttpDate(imf)\n  })\n\nawait bench.run()\n\nconsole.log(bench.name)\nconsole.table(bench.table())\n"
  },
  {
    "path": "benchmarks/wait.js",
    "content": "'use strict'\n\nconst os = require('node:os')\nconst path = require('node:path')\nconst waitOn = require('wait-on')\n\nconst socketPath = path.join(os.tmpdir(), 'undici.sock')\n\nlet resources\nif (process.env.PORT) {\n  resources = [`http-get://localhost:${process.env.PORT}/`]\n} else {\n  resources = [`http-get://unix:${socketPath}:/`]\n}\n\nwaitOn({\n  resources,\n  timeout: 5000\n}).catch((err) => {\n  console.error(err)\n  process.exitCode = 1\n})\n"
  },
  {
    "path": "benchmarks/webidl/webidl-is.mjs",
    "content": "import { bench, run, barplot } from 'mitata'\nimport { Headers, FormData } from '../../index.js'\nimport { webidl } from '../../lib/web/webidl/index.js'\n\nconst headers = new Headers()\nconst fd = new FormData()\n\nbarplot(() => {\n  bench('webidl.is.FormData (ok)', () => {\n    return webidl.is.FormData(fd)\n  })\n\n  bench('webidl.is.FormData (bad)', () => {\n    return !webidl.is.FormData(headers)\n  })\n\n  bench('instanceof (ok)', () => {\n    return fd instanceof FormData\n  })\n\n  bench('instanceof (bad)', () => {\n    return !(headers instanceof FormData)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/websocket/generate-mask.mjs",
    "content": "import { randomBytes } from 'node:crypto'\nimport { bench, summary, run } from 'mitata'\nimport { generateMask } from '../../lib/web/websocket/frame.js'\n\nsummary(() => {\n  bench('generateMask', () => generateMask())\n  bench('crypto.randomBytes(4)', () => randomBytes(4))\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/websocket/is-valid-subprotocol.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { isValidSubprotocol } from '../../lib/web/websocket/util.js'\n\nconst valid = 'valid'\nconst invalid = 'invalid '\n\ngroup('isValidSubprotocol', () => {\n  bench(`valid: ${valid}`, () => {\n    return isValidSubprotocol(valid)\n  })\n\n  bench(`invalid: ${invalid}`, () => {\n    return isValidSubprotocol(invalid)\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/websocket/messageevent.mjs",
    "content": "import { bench, group, run } from 'mitata'\nimport { createFastMessageEvent, MessageEvent as UndiciMessageEvent } from '../../lib/web/websocket/events.js'\n\nconst { port1, port2 } = new MessageChannel()\n\ngroup('MessageEvent instantiation', () => {\n  bench('undici - fast MessageEvent init', () => {\n    return createFastMessageEvent('event', { data: null, ports: [port1, port2] })\n  })\n\n  bench('undici - MessageEvent init', () => {\n    return new UndiciMessageEvent('event', { data: null, ports: [port1, port2] })\n  })\n\n  bench('global - MessageEvent init', () => {\n    // eslint-disable-next-line no-restricted-globals\n    return new MessageEvent('event', { data: null, ports: [port1, port2] })\n  })\n})\n\nawait run()\n"
  },
  {
    "path": "benchmarks/websocket-benchmark.mjs",
    "content": "// @ts-check\n\nimport { bench } from './_util/runner.js'\nimport { formatBytes } from './_util/index.js'\nimport { WebSocket, WebSocketStream } from '../index.js'\nimport { WebSocket as WsWebSocket } from 'ws'\n\n/**\n * @type {Record<string, { fn: (ws: any, binary: string | Uint8Array) => import('./_util/runner.js').BenchMarkHandler; connect: (url: string) => Promise<any>; binaries: (string | Uint8Array)[] }>}\n */\nconst experiments = {}\n/**\n * @type {Record<string, { bytes: number; binaryType: 'string' | 'binary' }>}\n */\nconst experimentsInfo = {}\n\n/**\n * @type {any[]}\n */\nconst connections = []\n\nconst binary = Buffer.alloc(256 * 1024, '_')\nconst binaries = [binary, binary.subarray(0, 256 * 1024).toString('utf-8')]\n\nexperiments['undici'] = {\n  fn: (ws, binary) => {\n    if (!(ws instanceof WebSocket)) {\n      throw new Error(\"'undici' websocket are expected.\")\n    }\n\n    return (ev) => {\n      ws.addEventListener(\n        'message',\n        () => {\n          ev.end()\n        },\n        { once: true }\n      )\n\n      ev.start()\n      ws.send(binary)\n    }\n  },\n\n  connect: async (url) => {\n    const ws = new WebSocket(url)\n\n    await /** @type {Promise<void>} */ (\n      new Promise((resolve, reject) => {\n        function onOpen () {\n          resolve()\n          ws.removeEventListener('open', onOpen)\n          ws.removeEventListener('error', onError)\n        }\n        function onError (err) {\n          reject(err)\n          ws.removeEventListener('open', onOpen)\n          ws.removeEventListener('error', onError)\n        }\n        ws.addEventListener('open', onOpen)\n        ws.addEventListener('error', onError)\n      })\n    )\n\n    // avoid create blob\n    ws.binaryType = 'arraybuffer'\n\n    return ws\n  },\n\n  binaries\n}\n\nexperiments['undici - stream'] = {\n  fn: (ws, binary) => {\n    /** @type {ReadableStreamDefaultReader<string | Uint8Array>} */\n    const reader = ws.reader\n    /** @type {WritableStreamDefaultWriter<string | BufferSource>} */\n    const writer = ws.writer\n\n    return async (ev) => {\n      ev.start()\n      await writer.write(binary)\n      await reader.read()\n      ev.end()\n    }\n  },\n\n  connect: async (url) => {\n    const ws = new WebSocketStream(url)\n\n    const { readable, writable } = await ws.opened\n    const reader = readable.getReader()\n    const writer = writable.getWriter()\n\n    // @ts-ignore\n    return { reader, writer, close: () => ws.close() }\n  },\n\n  binaries\n}\n\nexperiments['ws'] = {\n  fn: (ws, binary) => {\n    if (!(ws instanceof WsWebSocket)) {\n      throw new Error(\"'ws' websocket are expected.\")\n    }\n\n    return (ev) => {\n      ws.once('message', () => {\n        ev.end()\n      })\n      ev.start()\n      ws.send(binary)\n    }\n  },\n\n  connect: async (url) => {\n    const ws = new WsWebSocket(url, { maxPayload: 1024 * 1024 * 1024 })\n\n    await /** @type {Promise<void>} */ (\n      new Promise((resolve, reject) => {\n        function onOpen () {\n          resolve()\n          ws.off('open', onOpen)\n          ws.off('error', onError)\n        }\n        function onError (err) {\n          reject(err)\n          ws.off('open', onOpen)\n          ws.off('error', onError)\n        }\n        ws.on('open', onOpen)\n        ws.on('error', onError)\n      })\n    )\n\n    ws.binaryType = 'arraybuffer'\n\n    return ws\n  },\n\n  binaries\n}\n\nasync function init () {\n  /** @type {Record<string, import('./_util/runner.js').BenchMarkHandler>} */\n  const round = {}\n\n  const keys = Object.keys(experiments)\n\n  for (let i = 0; i < keys.length; ++i) {\n    const name = keys[i]\n\n    const { fn, connect, binaries } = experiments[name]\n\n    const ws = await connect('ws://localhost:8080')\n\n    const needShowBytes = binaries.length !== 2 || typeof binaries[0] === typeof binaries[1]\n    for (let i = 0; i < binaries.length; ++i) {\n      const binary = binaries[i]\n      const bytes = Buffer.byteLength(binary)\n\n      const binaryType = typeof binary === 'string' ? 'string' : 'binary'\n      const roundName = needShowBytes\n        ? `${name} [${formatBytes(bytes)} (${binaryType})]`\n        : `${name} [${binaryType}]`\n\n      round[roundName] = fn(ws, binary)\n      experimentsInfo[roundName] = { bytes, binaryType }\n    }\n\n    connections.push(ws)\n  }\n\n  return round\n}\n\ninit()\n  .then((round) => bench(round, {\n    minSamples: 2048\n  }))\n  .then((results) => {\n    print(results)\n\n    for (const ws of connections) {\n      ws.close()\n    }\n  }, (err) => {\n    process.nextTick((err) => {\n      throw err\n    }, err)\n  })\n\n/**\n * @param {{ name: string; average: number; iterationPerSecond: number; }[]} results\n */\nfunction print (results) {\n  for (const { name, average, iterationPerSecond } of results) {\n    const { bytes } = experimentsInfo[name]\n\n    console.log(\n      `${name}: transferred ${formatBytes((bytes / average) * 1e9)} Bytes/s (${iterationPerSecond.toFixed(4)} per/sec)`\n    )\n  }\n}\n\nexport {}\n"
  },
  {
    "path": "benchmarks/websocket-echo-server.mjs",
    "content": "import { Worker, isMainThread, parentPort, threadId } from 'node:worker_threads'\nimport { cpus } from 'node:os'\nimport url from 'node:url'\nimport uws from 'uWebSockets.js'\n\nconst __filename = url.fileURLToPath(import.meta.url)\n\nconst app = uws.App()\n\nif (isMainThread) {\n  for (let i = cpus().length - 1; i >= 0; --i) {\n    new Worker(__filename).on('message', (workerAppDescriptor) => {\n      app.addChildAppDescriptor(workerAppDescriptor)\n    })\n  }\n} else {\n  app\n    .ws('/*', {\n      compression: uws.DISABLED,\n      maxPayloadLength: 1024 * 1024 * 1024,\n      maxBackpressure: 1 * 1024 * 1024,\n      idleTimeout: 60,\n      message: (ws, message, isBinary) => {\n        /* Here we echo the message back, using compression if available */\n        const ok = ws.send(message, isBinary) // eslint-disable-line\n      }\n    })\n    .get('/*', (res, req) => {\n      /* It does Http as well */\n      res\n        .writeStatus('200 OK')\n        .end('Hello there!')\n    })\n\n  parentPort.postMessage(app.getDescriptor())\n}\n\napp.listen(8080, (listenSocket) => {\n  if (listenSocket) {\n    if (threadId === 0) {\n      console.log('Listening to port 8080')\n    } else {\n      console.log(`Listening to port 8080 from thread ${threadId}`)\n    }\n  }\n})\n"
  },
  {
    "path": "build/wasm.js",
    "content": "'use strict'\n\nconst WASM_BUILDER_CONTAINER = 'ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970' // v0.0.9\n\nconst { execSync, execFileSync } = require('node:child_process')\nconst { writeFileSync, readFileSync } = require('node:fs')\nconst { join, resolve } = require('node:path')\n\nconst ROOT = resolve(__dirname, '../')\nconst WASM_SRC = resolve(__dirname, '../deps/llhttp')\nconst WASM_OUT = resolve(__dirname, '../lib/llhttp')\n\n// These are defined by build environment\nconst WASM_CC = process.env.WASM_CC || 'clang'\nlet WASM_CFLAGS = process.env.WASM_CFLAGS || '--sysroot=/usr/share/wasi-sysroot -target wasm32-unknown-wasi'\nlet WASM_LDFLAGS = process.env.WASM_LDFLAGS || ''\nconst WASM_LDLIBS = process.env.WASM_LDLIBS || ''\nconst WASM_OPT = process.env.WASM_OPT || 'wasm-opt'\n\n// For compatibility with Node.js' `configure --shared-builtin-undici/undici-path ...`\nconst EXTERNAL_PATH = process.env.EXTERNAL_PATH\n\n// These are relevant for undici and should not be overridden\nWASM_CFLAGS += ' -Ofast -fno-exceptions -fvisibility=hidden -mexec-model=reactor'\nWASM_LDFLAGS += ' -Wl,-error-limit=0 -Wl,-O3 -Wl,--lto-O3 -Wl,--strip-all'\nWASM_LDFLAGS += ' -Wl,--allow-undefined -Wl,--export-dynamic -Wl,--export-table'\nWASM_LDFLAGS += ' -Wl,--export=malloc -Wl,--export=free -Wl,--no-entry'\n\nconst WASM_OPT_FLAGS = '-O4 --converge --strip-debug --strip-dwarf --strip-producers'\n\nconst writeWasmChunk = (path, dest) => {\n  const base64 = readFileSync(join(WASM_OUT, path)).toString('base64')\n  writeFileSync(join(WASM_OUT, dest), `'use strict'\n\nconst { Buffer } = require('node:buffer')\n\nconst wasmBase64 = '${base64}'\n\nlet wasmBuffer\n\nObject.defineProperty(module, 'exports', {\n  get: () => {\n    return wasmBuffer\n      ? wasmBuffer\n      : (wasmBuffer = Buffer.from(wasmBase64, 'base64'))\n  }\n})\n`)\n}\n\nlet platform = process.env.WASM_PLATFORM\nif (!platform && process.argv[2]) {\n  platform = execSync('docker info -f \"{{.OSType}}/{{.Architecture}}\"').toString().trim()\n}\n\nif (process.argv[2] === '--docker') {\n  let cmd = `docker run --rm --platform=${platform.toString().trim()} `\n  if (process.platform === 'linux') {\n    cmd += ` --user ${process.getuid()}:${process.getegid()}`\n  }\n\n  cmd += ` --mount type=bind,source=${ROOT}/lib/llhttp,target=/home/node/build/lib/llhttp \\\n           --mount type=bind,source=${ROOT}/build,target=/home/node/build/build \\\n           --mount type=bind,source=${ROOT}/deps,target=/home/node/build/deps \\\n           -t ${WASM_BUILDER_CONTAINER} node build/wasm.js`\n  console.log(`> ${cmd}\\n\\n`)\n  execSync(cmd, { stdio: 'inherit' })\n  process.exit(0) // eslint-disable-line n/no-process-exit\n}\n\nconst hasApk = (function () {\n  try { execSync('command -v apk'); return true } catch { return false }\n})()\nconst hasOptimizer = (function () {\n  try { execSync(`${WASM_OPT} --version`); return true } catch { return false }\n})()\nif (hasApk) {\n  // Gather information about the tools used for the build\n  const buildInfo = execSync('apk info -v').toString()\n  if (!buildInfo.includes('wasi-sdk')) {\n    throw new Error('Failed to generate build environment information')\n  }\n  console.log(buildInfo)\n}\n\n// Build wasm binary\nexecSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \\\n${join(WASM_SRC, 'src')}/*.c \\\n-I${join(WASM_SRC, 'include')} \\\n-o ${join(WASM_OUT, 'llhttp.wasm')} \\\n${WASM_LDLIBS}`, { stdio: 'inherit' })\n\nif (hasOptimizer) {\n  execSync(`${WASM_OPT} ${WASM_OPT_FLAGS} -o ${join(WASM_OUT, 'llhttp.wasm')} ${join(WASM_OUT, 'llhttp.wasm')}`, { stdio: 'inherit' })\n}\nwriteWasmChunk('llhttp.wasm', 'llhttp-wasm.js')\n\n// Build wasm simd binary\nexecSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \\\n${join(WASM_SRC, 'src')}/*.c \\\n-I${join(WASM_SRC, 'include')} \\\n-o ${join(WASM_OUT, 'llhttp_simd.wasm')} \\\n${WASM_LDLIBS}`, { stdio: 'inherit' })\n\nif (hasOptimizer) {\n  // Split WASM_OPT_FLAGS into an array, if not empty\n  const wasmOptFlagsArray = WASM_OPT_FLAGS ? WASM_OPT_FLAGS.split(/\\s+/).filter(Boolean) : []\n  execFileSync(\n    WASM_OPT,\n    [\n      ...wasmOptFlagsArray,\n      '--enable-simd',\n      '-o',\n      join(WASM_OUT, 'llhttp_simd.wasm'),\n      join(WASM_OUT, 'llhttp_simd.wasm')\n    ],\n    { stdio: 'inherit' }\n  )\n}\nwriteWasmChunk('llhttp_simd.wasm', 'llhttp_simd-wasm.js')\n\n// For compatibility with Node.js' `configure --shared-builtin-undici/undici-path ...`\nif (EXTERNAL_PATH) {\n  writeFileSync(join(ROOT, 'loader.js'), `\n'use strict'\nglobalThis.__UNDICI_IS_NODE__ = true\nmodule.exports = require('node:module').createRequire('${EXTERNAL_PATH}/loader.js')('./index-fetch.js')\ndelete globalThis.__UNDICI_IS_NODE__\n`)\n}\n"
  },
  {
    "path": "deps/llhttp/include/llhttp.h",
    "content": "\n#ifndef INCLUDE_LLHTTP_H_\n#define INCLUDE_LLHTTP_H_\n\n#define LLHTTP_VERSION_MAJOR 9\n#define LLHTTP_VERSION_MINOR 3\n#define LLHTTP_VERSION_PATCH 0\n\n#ifndef INCLUDE_LLHTTP_ITSELF_H_\n#define INCLUDE_LLHTTP_ITSELF_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\ntypedef struct llhttp__internal_s llhttp__internal_t;\nstruct llhttp__internal_s {\n  int32_t _index;\n  void* _span_pos0;\n  void* _span_cb0;\n  int32_t error;\n  const char* reason;\n  const char* error_pos;\n  void* data;\n  void* _current;\n  uint64_t content_length;\n  uint8_t type;\n  uint8_t method;\n  uint8_t http_major;\n  uint8_t http_minor;\n  uint8_t header_state;\n  uint16_t lenient_flags;\n  uint8_t upgrade;\n  uint8_t finish;\n  uint16_t flags;\n  uint16_t status_code;\n  uint8_t initial_message_completed;\n  void* settings;\n};\n\nint llhttp__internal_init(llhttp__internal_t* s);\nint llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* INCLUDE_LLHTTP_ITSELF_H_ */\n\n\n#ifndef LLLLHTTP_C_HEADERS_\n#define LLLLHTTP_C_HEADERS_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum llhttp_errno {\n  HPE_OK = 0,\n  HPE_INTERNAL = 1,\n  HPE_STRICT = 2,\n  HPE_CR_EXPECTED = 25,\n  HPE_LF_EXPECTED = 3,\n  HPE_UNEXPECTED_CONTENT_LENGTH = 4,\n  HPE_UNEXPECTED_SPACE = 30,\n  HPE_CLOSED_CONNECTION = 5,\n  HPE_INVALID_METHOD = 6,\n  HPE_INVALID_URL = 7,\n  HPE_INVALID_CONSTANT = 8,\n  HPE_INVALID_VERSION = 9,\n  HPE_INVALID_HEADER_TOKEN = 10,\n  HPE_INVALID_CONTENT_LENGTH = 11,\n  HPE_INVALID_CHUNK_SIZE = 12,\n  HPE_INVALID_STATUS = 13,\n  HPE_INVALID_EOF_STATE = 14,\n  HPE_INVALID_TRANSFER_ENCODING = 15,\n  HPE_CB_MESSAGE_BEGIN = 16,\n  HPE_CB_HEADERS_COMPLETE = 17,\n  HPE_CB_MESSAGE_COMPLETE = 18,\n  HPE_CB_CHUNK_HEADER = 19,\n  HPE_CB_CHUNK_COMPLETE = 20,\n  HPE_PAUSED = 21,\n  HPE_PAUSED_UPGRADE = 22,\n  HPE_PAUSED_H2_UPGRADE = 23,\n  HPE_USER = 24,\n  HPE_CB_URL_COMPLETE = 26,\n  HPE_CB_STATUS_COMPLETE = 27,\n  HPE_CB_METHOD_COMPLETE = 32,\n  HPE_CB_VERSION_COMPLETE = 33,\n  HPE_CB_HEADER_FIELD_COMPLETE = 28,\n  HPE_CB_HEADER_VALUE_COMPLETE = 29,\n  HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,\n  HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,\n  HPE_CB_RESET = 31,\n  HPE_CB_PROTOCOL_COMPLETE = 38\n};\ntypedef enum llhttp_errno llhttp_errno_t;\n\nenum llhttp_flags {\n  F_CONNECTION_KEEP_ALIVE = 0x1,\n  F_CONNECTION_CLOSE = 0x2,\n  F_CONNECTION_UPGRADE = 0x4,\n  F_CHUNKED = 0x8,\n  F_UPGRADE = 0x10,\n  F_CONTENT_LENGTH = 0x20,\n  F_SKIPBODY = 0x40,\n  F_TRAILING = 0x80,\n  F_TRANSFER_ENCODING = 0x200\n};\ntypedef enum llhttp_flags llhttp_flags_t;\n\nenum llhttp_lenient_flags {\n  LENIENT_HEADERS = 0x1,\n  LENIENT_CHUNKED_LENGTH = 0x2,\n  LENIENT_KEEP_ALIVE = 0x4,\n  LENIENT_TRANSFER_ENCODING = 0x8,\n  LENIENT_VERSION = 0x10,\n  LENIENT_DATA_AFTER_CLOSE = 0x20,\n  LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,\n  LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,\n  LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,\n  LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200\n};\ntypedef enum llhttp_lenient_flags llhttp_lenient_flags_t;\n\nenum llhttp_type {\n  HTTP_BOTH = 0,\n  HTTP_REQUEST = 1,\n  HTTP_RESPONSE = 2\n};\ntypedef enum llhttp_type llhttp_type_t;\n\nenum llhttp_finish {\n  HTTP_FINISH_SAFE = 0,\n  HTTP_FINISH_SAFE_WITH_CB = 1,\n  HTTP_FINISH_UNSAFE = 2\n};\ntypedef enum llhttp_finish llhttp_finish_t;\n\nenum llhttp_method {\n  HTTP_DELETE = 0,\n  HTTP_GET = 1,\n  HTTP_HEAD = 2,\n  HTTP_POST = 3,\n  HTTP_PUT = 4,\n  HTTP_CONNECT = 5,\n  HTTP_OPTIONS = 6,\n  HTTP_TRACE = 7,\n  HTTP_COPY = 8,\n  HTTP_LOCK = 9,\n  HTTP_MKCOL = 10,\n  HTTP_MOVE = 11,\n  HTTP_PROPFIND = 12,\n  HTTP_PROPPATCH = 13,\n  HTTP_SEARCH = 14,\n  HTTP_UNLOCK = 15,\n  HTTP_BIND = 16,\n  HTTP_REBIND = 17,\n  HTTP_UNBIND = 18,\n  HTTP_ACL = 19,\n  HTTP_REPORT = 20,\n  HTTP_MKACTIVITY = 21,\n  HTTP_CHECKOUT = 22,\n  HTTP_MERGE = 23,\n  HTTP_MSEARCH = 24,\n  HTTP_NOTIFY = 25,\n  HTTP_SUBSCRIBE = 26,\n  HTTP_UNSUBSCRIBE = 27,\n  HTTP_PATCH = 28,\n  HTTP_PURGE = 29,\n  HTTP_MKCALENDAR = 30,\n  HTTP_LINK = 31,\n  HTTP_UNLINK = 32,\n  HTTP_SOURCE = 33,\n  HTTP_PRI = 34,\n  HTTP_DESCRIBE = 35,\n  HTTP_ANNOUNCE = 36,\n  HTTP_SETUP = 37,\n  HTTP_PLAY = 38,\n  HTTP_PAUSE = 39,\n  HTTP_TEARDOWN = 40,\n  HTTP_GET_PARAMETER = 41,\n  HTTP_SET_PARAMETER = 42,\n  HTTP_REDIRECT = 43,\n  HTTP_RECORD = 44,\n  HTTP_FLUSH = 45,\n  HTTP_QUERY = 46\n};\ntypedef enum llhttp_method llhttp_method_t;\n\nenum llhttp_status {\n  HTTP_STATUS_CONTINUE = 100,\n  HTTP_STATUS_SWITCHING_PROTOCOLS = 101,\n  HTTP_STATUS_PROCESSING = 102,\n  HTTP_STATUS_EARLY_HINTS = 103,\n  HTTP_STATUS_RESPONSE_IS_STALE = 110,\n  HTTP_STATUS_REVALIDATION_FAILED = 111,\n  HTTP_STATUS_DISCONNECTED_OPERATION = 112,\n  HTTP_STATUS_HEURISTIC_EXPIRATION = 113,\n  HTTP_STATUS_MISCELLANEOUS_WARNING = 199,\n  HTTP_STATUS_OK = 200,\n  HTTP_STATUS_CREATED = 201,\n  HTTP_STATUS_ACCEPTED = 202,\n  HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,\n  HTTP_STATUS_NO_CONTENT = 204,\n  HTTP_STATUS_RESET_CONTENT = 205,\n  HTTP_STATUS_PARTIAL_CONTENT = 206,\n  HTTP_STATUS_MULTI_STATUS = 207,\n  HTTP_STATUS_ALREADY_REPORTED = 208,\n  HTTP_STATUS_TRANSFORMATION_APPLIED = 214,\n  HTTP_STATUS_IM_USED = 226,\n  HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,\n  HTTP_STATUS_MULTIPLE_CHOICES = 300,\n  HTTP_STATUS_MOVED_PERMANENTLY = 301,\n  HTTP_STATUS_FOUND = 302,\n  HTTP_STATUS_SEE_OTHER = 303,\n  HTTP_STATUS_NOT_MODIFIED = 304,\n  HTTP_STATUS_USE_PROXY = 305,\n  HTTP_STATUS_SWITCH_PROXY = 306,\n  HTTP_STATUS_TEMPORARY_REDIRECT = 307,\n  HTTP_STATUS_PERMANENT_REDIRECT = 308,\n  HTTP_STATUS_BAD_REQUEST = 400,\n  HTTP_STATUS_UNAUTHORIZED = 401,\n  HTTP_STATUS_PAYMENT_REQUIRED = 402,\n  HTTP_STATUS_FORBIDDEN = 403,\n  HTTP_STATUS_NOT_FOUND = 404,\n  HTTP_STATUS_METHOD_NOT_ALLOWED = 405,\n  HTTP_STATUS_NOT_ACCEPTABLE = 406,\n  HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,\n  HTTP_STATUS_REQUEST_TIMEOUT = 408,\n  HTTP_STATUS_CONFLICT = 409,\n  HTTP_STATUS_GONE = 410,\n  HTTP_STATUS_LENGTH_REQUIRED = 411,\n  HTTP_STATUS_PRECONDITION_FAILED = 412,\n  HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,\n  HTTP_STATUS_URI_TOO_LONG = 414,\n  HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,\n  HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,\n  HTTP_STATUS_EXPECTATION_FAILED = 417,\n  HTTP_STATUS_IM_A_TEAPOT = 418,\n  HTTP_STATUS_PAGE_EXPIRED = 419,\n  HTTP_STATUS_ENHANCE_YOUR_CALM = 420,\n  HTTP_STATUS_MISDIRECTED_REQUEST = 421,\n  HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,\n  HTTP_STATUS_LOCKED = 423,\n  HTTP_STATUS_FAILED_DEPENDENCY = 424,\n  HTTP_STATUS_TOO_EARLY = 425,\n  HTTP_STATUS_UPGRADE_REQUIRED = 426,\n  HTTP_STATUS_PRECONDITION_REQUIRED = 428,\n  HTTP_STATUS_TOO_MANY_REQUESTS = 429,\n  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,\n  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,\n  HTTP_STATUS_LOGIN_TIMEOUT = 440,\n  HTTP_STATUS_NO_RESPONSE = 444,\n  HTTP_STATUS_RETRY_WITH = 449,\n  HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,\n  HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,\n  HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,\n  HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,\n  HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,\n  HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,\n  HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,\n  HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,\n  HTTP_STATUS_INVALID_TOKEN = 498,\n  HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,\n  HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,\n  HTTP_STATUS_NOT_IMPLEMENTED = 501,\n  HTTP_STATUS_BAD_GATEWAY = 502,\n  HTTP_STATUS_SERVICE_UNAVAILABLE = 503,\n  HTTP_STATUS_GATEWAY_TIMEOUT = 504,\n  HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,\n  HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,\n  HTTP_STATUS_INSUFFICIENT_STORAGE = 507,\n  HTTP_STATUS_LOOP_DETECTED = 508,\n  HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,\n  HTTP_STATUS_NOT_EXTENDED = 510,\n  HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,\n  HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,\n  HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,\n  HTTP_STATUS_CONNECTION_TIMEOUT = 522,\n  HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,\n  HTTP_STATUS_TIMEOUT_OCCURED = 524,\n  HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,\n  HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,\n  HTTP_STATUS_RAILGUN_ERROR = 527,\n  HTTP_STATUS_SITE_IS_OVERLOADED = 529,\n  HTTP_STATUS_SITE_IS_FROZEN = 530,\n  HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,\n  HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,\n  HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599\n};\ntypedef enum llhttp_status llhttp_status_t;\n\n#define HTTP_ERRNO_MAP(XX) \\\n  XX(0, OK, OK) \\\n  XX(1, INTERNAL, INTERNAL) \\\n  XX(2, STRICT, STRICT) \\\n  XX(25, CR_EXPECTED, CR_EXPECTED) \\\n  XX(3, LF_EXPECTED, LF_EXPECTED) \\\n  XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \\\n  XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \\\n  XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \\\n  XX(6, INVALID_METHOD, INVALID_METHOD) \\\n  XX(7, INVALID_URL, INVALID_URL) \\\n  XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \\\n  XX(9, INVALID_VERSION, INVALID_VERSION) \\\n  XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \\\n  XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \\\n  XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \\\n  XX(13, INVALID_STATUS, INVALID_STATUS) \\\n  XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \\\n  XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \\\n  XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \\\n  XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \\\n  XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \\\n  XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \\\n  XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \\\n  XX(21, PAUSED, PAUSED) \\\n  XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \\\n  XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \\\n  XX(24, USER, USER) \\\n  XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \\\n  XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \\\n  XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \\\n  XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \\\n  XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \\\n  XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \\\n  XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \\\n  XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \\\n  XX(31, CB_RESET, CB_RESET) \\\n  XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE) \\\n\n\n#define HTTP_METHOD_MAP(XX) \\\n  XX(0, DELETE, DELETE) \\\n  XX(1, GET, GET) \\\n  XX(2, HEAD, HEAD) \\\n  XX(3, POST, POST) \\\n  XX(4, PUT, PUT) \\\n  XX(5, CONNECT, CONNECT) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(7, TRACE, TRACE) \\\n  XX(8, COPY, COPY) \\\n  XX(9, LOCK, LOCK) \\\n  XX(10, MKCOL, MKCOL) \\\n  XX(11, MOVE, MOVE) \\\n  XX(12, PROPFIND, PROPFIND) \\\n  XX(13, PROPPATCH, PROPPATCH) \\\n  XX(14, SEARCH, SEARCH) \\\n  XX(15, UNLOCK, UNLOCK) \\\n  XX(16, BIND, BIND) \\\n  XX(17, REBIND, REBIND) \\\n  XX(18, UNBIND, UNBIND) \\\n  XX(19, ACL, ACL) \\\n  XX(20, REPORT, REPORT) \\\n  XX(21, MKACTIVITY, MKACTIVITY) \\\n  XX(22, CHECKOUT, CHECKOUT) \\\n  XX(23, MERGE, MERGE) \\\n  XX(24, MSEARCH, M-SEARCH) \\\n  XX(25, NOTIFY, NOTIFY) \\\n  XX(26, SUBSCRIBE, SUBSCRIBE) \\\n  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \\\n  XX(28, PATCH, PATCH) \\\n  XX(29, PURGE, PURGE) \\\n  XX(30, MKCALENDAR, MKCALENDAR) \\\n  XX(31, LINK, LINK) \\\n  XX(32, UNLINK, UNLINK) \\\n  XX(33, SOURCE, SOURCE) \\\n  XX(46, QUERY, QUERY) \\\n\n\n#define RTSP_METHOD_MAP(XX) \\\n  XX(1, GET, GET) \\\n  XX(3, POST, POST) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(35, DESCRIBE, DESCRIBE) \\\n  XX(36, ANNOUNCE, ANNOUNCE) \\\n  XX(37, SETUP, SETUP) \\\n  XX(38, PLAY, PLAY) \\\n  XX(39, PAUSE, PAUSE) \\\n  XX(40, TEARDOWN, TEARDOWN) \\\n  XX(41, GET_PARAMETER, GET_PARAMETER) \\\n  XX(42, SET_PARAMETER, SET_PARAMETER) \\\n  XX(43, REDIRECT, REDIRECT) \\\n  XX(44, RECORD, RECORD) \\\n  XX(45, FLUSH, FLUSH) \\\n\n\n#define HTTP_ALL_METHOD_MAP(XX) \\\n  XX(0, DELETE, DELETE) \\\n  XX(1, GET, GET) \\\n  XX(2, HEAD, HEAD) \\\n  XX(3, POST, POST) \\\n  XX(4, PUT, PUT) \\\n  XX(5, CONNECT, CONNECT) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(7, TRACE, TRACE) \\\n  XX(8, COPY, COPY) \\\n  XX(9, LOCK, LOCK) \\\n  XX(10, MKCOL, MKCOL) \\\n  XX(11, MOVE, MOVE) \\\n  XX(12, PROPFIND, PROPFIND) \\\n  XX(13, PROPPATCH, PROPPATCH) \\\n  XX(14, SEARCH, SEARCH) \\\n  XX(15, UNLOCK, UNLOCK) \\\n  XX(16, BIND, BIND) \\\n  XX(17, REBIND, REBIND) \\\n  XX(18, UNBIND, UNBIND) \\\n  XX(19, ACL, ACL) \\\n  XX(20, REPORT, REPORT) \\\n  XX(21, MKACTIVITY, MKACTIVITY) \\\n  XX(22, CHECKOUT, CHECKOUT) \\\n  XX(23, MERGE, MERGE) \\\n  XX(24, MSEARCH, M-SEARCH) \\\n  XX(25, NOTIFY, NOTIFY) \\\n  XX(26, SUBSCRIBE, SUBSCRIBE) \\\n  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \\\n  XX(28, PATCH, PATCH) \\\n  XX(29, PURGE, PURGE) \\\n  XX(30, MKCALENDAR, MKCALENDAR) \\\n  XX(31, LINK, LINK) \\\n  XX(32, UNLINK, UNLINK) \\\n  XX(33, SOURCE, SOURCE) \\\n  XX(34, PRI, PRI) \\\n  XX(35, DESCRIBE, DESCRIBE) \\\n  XX(36, ANNOUNCE, ANNOUNCE) \\\n  XX(37, SETUP, SETUP) \\\n  XX(38, PLAY, PLAY) \\\n  XX(39, PAUSE, PAUSE) \\\n  XX(40, TEARDOWN, TEARDOWN) \\\n  XX(41, GET_PARAMETER, GET_PARAMETER) \\\n  XX(42, SET_PARAMETER, SET_PARAMETER) \\\n  XX(43, REDIRECT, REDIRECT) \\\n  XX(44, RECORD, RECORD) \\\n  XX(45, FLUSH, FLUSH) \\\n  XX(46, QUERY, QUERY) \\\n\n\n#define HTTP_STATUS_MAP(XX) \\\n  XX(100, CONTINUE, CONTINUE) \\\n  XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \\\n  XX(102, PROCESSING, PROCESSING) \\\n  XX(103, EARLY_HINTS, EARLY_HINTS) \\\n  XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \\\n  XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \\\n  XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \\\n  XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \\\n  XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \\\n  XX(200, OK, OK) \\\n  XX(201, CREATED, CREATED) \\\n  XX(202, ACCEPTED, ACCEPTED) \\\n  XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \\\n  XX(204, NO_CONTENT, NO_CONTENT) \\\n  XX(205, RESET_CONTENT, RESET_CONTENT) \\\n  XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \\\n  XX(207, MULTI_STATUS, MULTI_STATUS) \\\n  XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \\\n  XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \\\n  XX(226, IM_USED, IM_USED) \\\n  XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \\\n  XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \\\n  XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \\\n  XX(302, FOUND, FOUND) \\\n  XX(303, SEE_OTHER, SEE_OTHER) \\\n  XX(304, NOT_MODIFIED, NOT_MODIFIED) \\\n  XX(305, USE_PROXY, USE_PROXY) \\\n  XX(306, SWITCH_PROXY, SWITCH_PROXY) \\\n  XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \\\n  XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \\\n  XX(400, BAD_REQUEST, BAD_REQUEST) \\\n  XX(401, UNAUTHORIZED, UNAUTHORIZED) \\\n  XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \\\n  XX(403, FORBIDDEN, FORBIDDEN) \\\n  XX(404, NOT_FOUND, NOT_FOUND) \\\n  XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \\\n  XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \\\n  XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \\\n  XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \\\n  XX(409, CONFLICT, CONFLICT) \\\n  XX(410, GONE, GONE) \\\n  XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \\\n  XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \\\n  XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \\\n  XX(414, URI_TOO_LONG, URI_TOO_LONG) \\\n  XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \\\n  XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \\\n  XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \\\n  XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \\\n  XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \\\n  XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \\\n  XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \\\n  XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \\\n  XX(423, LOCKED, LOCKED) \\\n  XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \\\n  XX(425, TOO_EARLY, TOO_EARLY) \\\n  XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \\\n  XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \\\n  XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \\\n  XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \\\n  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \\\n  XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \\\n  XX(444, NO_RESPONSE, NO_RESPONSE) \\\n  XX(449, RETRY_WITH, RETRY_WITH) \\\n  XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \\\n  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \\\n  XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \\\n  XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \\\n  XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \\\n  XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \\\n  XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \\\n  XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \\\n  XX(498, INVALID_TOKEN, INVALID_TOKEN) \\\n  XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \\\n  XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \\\n  XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \\\n  XX(502, BAD_GATEWAY, BAD_GATEWAY) \\\n  XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \\\n  XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \\\n  XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \\\n  XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \\\n  XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \\\n  XX(508, LOOP_DETECTED, LOOP_DETECTED) \\\n  XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \\\n  XX(510, NOT_EXTENDED, NOT_EXTENDED) \\\n  XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \\\n  XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \\\n  XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \\\n  XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \\\n  XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \\\n  XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \\\n  XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \\\n  XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \\\n  XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \\\n  XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \\\n  XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \\\n  XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \\\n  XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \\\n  XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \\\n\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* LLLLHTTP_C_HEADERS_ */\n\n\n#ifndef INCLUDE_LLHTTP_API_H_\n#define INCLUDE_LLHTTP_API_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <stddef.h>\n\n#if defined(__wasm__)\n#define LLHTTP_EXPORT __attribute__((visibility(\"default\")))\n#elif defined(_WIN32)\n#define LLHTTP_EXPORT __declspec(dllexport)\n#else\n#define LLHTTP_EXPORT\n#endif\n\ntypedef llhttp__internal_t llhttp_t;\ntypedef struct llhttp_settings_s llhttp_settings_t;\n\ntypedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);\ntypedef int (*llhttp_cb)(llhttp_t*);\n\nstruct llhttp_settings_s {\n  /* Possible return values 0, -1, `HPE_PAUSED` */\n  llhttp_cb      on_message_begin;\n\n  /* Possible return values 0, -1, HPE_USER */\n  llhttp_data_cb on_protocol;\n  llhttp_data_cb on_url;\n  llhttp_data_cb on_status;\n  llhttp_data_cb on_method;\n  llhttp_data_cb on_version;\n  llhttp_data_cb on_header_field;\n  llhttp_data_cb on_header_value;\n  llhttp_data_cb      on_chunk_extension_name;\n  llhttp_data_cb      on_chunk_extension_value;\n\n  /* Possible return values:\n   * 0  - Proceed normally\n   * 1  - Assume that request/response has no body, and proceed to parsing the\n   *      next message\n   * 2  - Assume absence of body (as above) and make `llhttp_execute()` return\n   *      `HPE_PAUSED_UPGRADE`\n   * -1 - Error\n   * `HPE_PAUSED`\n   */\n  llhttp_cb      on_headers_complete;\n\n  /* Possible return values 0, -1, HPE_USER */\n  llhttp_data_cb on_body;\n\n  /* Possible return values 0, -1, `HPE_PAUSED` */\n  llhttp_cb      on_message_complete;\n  llhttp_cb      on_protocol_complete;\n  llhttp_cb      on_url_complete;\n  llhttp_cb      on_status_complete;\n  llhttp_cb      on_method_complete;\n  llhttp_cb      on_version_complete;\n  llhttp_cb      on_header_field_complete;\n  llhttp_cb      on_header_value_complete;\n  llhttp_cb      on_chunk_extension_name_complete;\n  llhttp_cb      on_chunk_extension_value_complete;\n\n  /* When on_chunk_header is called, the current chunk length is stored\n   * in parser->content_length.\n   * Possible return values 0, -1, `HPE_PAUSED`\n   */\n  llhttp_cb      on_chunk_header;\n  llhttp_cb      on_chunk_complete;\n  llhttp_cb      on_reset;\n};\n\n/* Initialize the parser with specific type and user settings.\n *\n * NOTE: lifetime of `settings` has to be at least the same as the lifetime of\n * the `parser` here. In practice, `settings` has to be either a static\n * variable or be allocated with `malloc`, `new`, etc.\n */\nLLHTTP_EXPORT\nvoid llhttp_init(llhttp_t* parser, llhttp_type_t type,\n                 const llhttp_settings_t* settings);\n\nLLHTTP_EXPORT\nllhttp_t* llhttp_alloc(llhttp_type_t type);\n\nLLHTTP_EXPORT\nvoid llhttp_free(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_type(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_http_major(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_http_minor(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_method(llhttp_t* parser);\n\nLLHTTP_EXPORT\nint llhttp_get_status_code(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_upgrade(llhttp_t* parser);\n\n/* Reset an already initialized parser back to the start state, preserving the\n * existing parser type, callback settings, user data, and lenient flags.\n */\nLLHTTP_EXPORT\nvoid llhttp_reset(llhttp_t* parser);\n\n/* Initialize the settings object */\nLLHTTP_EXPORT\nvoid llhttp_settings_init(llhttp_settings_t* settings);\n\n/* Parse full or partial request/response, invoking user callbacks along the\n * way.\n *\n * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing\n * interrupts, and such errno is returned from `llhttp_execute()`. If\n * `HPE_PAUSED` was used as a errno, the execution can be resumed with\n * `llhttp_resume()` call.\n *\n * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`\n * is returned after fully parsing the request/response. If the user wishes to\n * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.\n *\n * NOTE: if this function ever returns a non-pause type error, it will continue\n * to return the same error upon each successive call up until `llhttp_init()`\n * is called.\n */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);\n\n/* This method should be called when the other side has no further bytes to\n * send (e.g. shutdown of readable side of the TCP connection.)\n *\n * Requests without `Content-Length` and other messages might require treating\n * all incoming bytes as the part of the body, up to the last byte of the\n * connection. This method will invoke `on_message_complete()` callback if the\n * request was terminated safely. Otherwise a error code would be returned.\n */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_finish(llhttp_t* parser);\n\n/* Returns `1` if the incoming message is parsed until the last byte, and has\n * to be completed by calling `llhttp_finish()` on EOF\n */\nLLHTTP_EXPORT\nint llhttp_message_needs_eof(const llhttp_t* parser);\n\n/* Returns `1` if there might be any other messages following the last that was\n * successfully parsed.\n */\nLLHTTP_EXPORT\nint llhttp_should_keep_alive(const llhttp_t* parser);\n\n/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set\n * appropriate error reason.\n *\n * Important: do not call this from user callbacks! User callbacks must return\n * `HPE_PAUSED` if pausing is required.\n */\nLLHTTP_EXPORT\nvoid llhttp_pause(llhttp_t* parser);\n\n/* Might be called to resume the execution after the pause in user's callback.\n * See `llhttp_execute()` above for details.\n *\n * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.\n */\nLLHTTP_EXPORT\nvoid llhttp_resume(llhttp_t* parser);\n\n/* Might be called to resume the execution after the pause in user's callback.\n * See `llhttp_execute()` above for details.\n *\n * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`\n */\nLLHTTP_EXPORT\nvoid llhttp_resume_after_upgrade(llhttp_t* parser);\n\n/* Returns the latest return error */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_get_errno(const llhttp_t* parser);\n\n/* Returns the verbal explanation of the latest returned error.\n *\n * Note: User callback should set error reason when returning the error. See\n * `llhttp_set_error_reason()` for details.\n */\nLLHTTP_EXPORT\nconst char* llhttp_get_error_reason(const llhttp_t* parser);\n\n/* Assign verbal description to the returned error. Must be called in user\n * callbacks right before returning the errno.\n *\n * Note: `HPE_USER` error code might be useful in user callbacks.\n */\nLLHTTP_EXPORT\nvoid llhttp_set_error_reason(llhttp_t* parser, const char* reason);\n\n/* Returns the pointer to the last parsed byte before the returned error. The\n * pointer is relative to the `data` argument of `llhttp_execute()`.\n *\n * Note: this method might be useful for counting the number of parsed bytes.\n */\nLLHTTP_EXPORT\nconst char* llhttp_get_error_pos(const llhttp_t* parser);\n\n/* Returns textual name of error code */\nLLHTTP_EXPORT\nconst char* llhttp_errno_name(llhttp_errno_t err);\n\n/* Returns textual name of HTTP method */\nLLHTTP_EXPORT\nconst char* llhttp_method_name(llhttp_method_t method);\n\n/* Returns textual name of HTTP status */\nLLHTTP_EXPORT\nconst char* llhttp_status_name(llhttp_status_t status);\n\n/* Enables/disables lenient header value parsing (disabled by default).\n *\n * Lenient parsing disables header value token checks, extending llhttp's\n * protocol support to highly non-compliant clients/server. No\n * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when\n * lenient parsing is \"on\".\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_headers(llhttp_t* parser, int enabled);\n\n\n/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and\n * `Content-Length` headers (disabled by default).\n *\n * Normally `llhttp` would error when `Transfer-Encoding` is present in\n * conjunction with `Content-Length`. This error is important to prevent HTTP\n * request smuggling, but may be less desirable for small number of cases\n * involving legacy servers.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);\n\n\n/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0\n * requests responses.\n *\n * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)\n * the HTTP request/response after the request/response with `Connection: close`\n * and `Content-Length`. This is important to prevent cache poisoning attacks,\n * but might interact badly with outdated and insecure clients. With this flag\n * the extra request/response will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * poisoning attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of `Transfer-Encoding` header.\n *\n * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value\n * and another value after it (either in a single header or in multiple\n * headers whose value are internally joined using `, `).\n * This is mandated by the spec to reliably determine request body size and thus\n * avoid request smuggling.\n * With this flag the extra value will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of HTTP version.\n *\n * Normally `llhttp` would error when the HTTP version in the request or status line\n * is not `0.9`, `1.0`, `1.1` or `2.0`.\n * With this flag the invalid value will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will allow unsupported\n * HTTP versions. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_version(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of additional data received after a message ends\n * and keep-alive is disabled.\n *\n * Normally `llhttp` would error when additional unexpected data is received if the message\n * contains the `Connection` header with `close` value.\n * With this flag the extra data will discarded without throwing an error.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * poisoning attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of incomplete CRLF sequences.\n *\n * Normally `llhttp` would error when a CR is not followed by LF when terminating the\n * request line, the status line, the headers or a chunk header.\n * With this flag only a CR is required to terminate such sections.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);\n\n/*\n * Enables/disables lenient handling of line separators.\n *\n * Normally `llhttp` would error when a LF is not preceded by CR when terminating the\n * request line, the status line, the headers, a chunk header or a chunk data.\n * With this flag only a LF is required to terminate such sections.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of chunks not separated via CRLF.\n *\n * Normally `llhttp` would error when after a chunk data a CRLF is missing before\n * starting a new chunk.\n * With this flag the new chunk can start immediately after the previous one.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of spaces after chunk size.\n *\n * Normally `llhttp` would error when after a chunk size is followed by one or more\n * spaces are present instead of a CRLF or `;`.\n * With this flag this check is disabled.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* INCLUDE_LLHTTP_API_H_ */\n\n\n#endif  /* INCLUDE_LLHTTP_H_ */\n"
  },
  {
    "path": "deps/llhttp/src/api.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"llhttp.h\"\n\n#define CALLBACK_MAYBE(PARSER, NAME)                                          \\\n  do {                                                                        \\\n    const llhttp_settings_t* settings;                                        \\\n    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \\\n    if (settings == NULL || settings->NAME == NULL) {                         \\\n      err = 0;                                                                \\\n      break;                                                                  \\\n    }                                                                         \\\n    err = settings->NAME((PARSER));                                           \\\n  } while (0)\n\n#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN)                         \\\n  do {                                                                        \\\n    const llhttp_settings_t* settings;                                        \\\n    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \\\n    if (settings == NULL || settings->NAME == NULL) {                         \\\n      err = 0;                                                                \\\n      break;                                                                  \\\n    }                                                                         \\\n    err = settings->NAME((PARSER), (START), (LEN));                           \\\n    if (err == -1) {                                                          \\\n      err = HPE_USER;                                                         \\\n      llhttp_set_error_reason((PARSER), \"Span callback error in \" #NAME);     \\\n    }                                                                         \\\n  } while (0)\n\nvoid llhttp_init(llhttp_t* parser, llhttp_type_t type,\n                 const llhttp_settings_t* settings) {\n  llhttp__internal_init(parser);\n\n  parser->type = type;\n  parser->settings = (void*) settings;\n}\n\n\n#if defined(__wasm__)\n\nextern int wasm_on_message_begin(llhttp_t * p);\nextern int wasm_on_url(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_status(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_headers_complete(llhttp_t * p, int status_code,\n                                    uint8_t upgrade, int should_keep_alive);\nextern int wasm_on_body(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_message_complete(llhttp_t * p);\n\nstatic int wasm_on_headers_complete_wrap(llhttp_t* p) {\n  return wasm_on_headers_complete(p, p->status_code, p->upgrade,\n                                  llhttp_should_keep_alive(p));\n}\n\nconst llhttp_settings_t wasm_settings = {\n  .on_message_begin = wasm_on_message_begin,\n  .on_url = wasm_on_url,\n  .on_status = wasm_on_status,\n  .on_header_field = wasm_on_header_field,\n  .on_header_value = wasm_on_header_value,\n  .on_headers_complete = wasm_on_headers_complete_wrap,\n  .on_body = wasm_on_body,\n  .on_message_complete = wasm_on_message_complete,\n};\n\n\nllhttp_t* llhttp_alloc(llhttp_type_t type) {\n  llhttp_t* parser = malloc(sizeof(llhttp_t));\n  llhttp_init(parser, type, &wasm_settings);\n  return parser;\n}\n\nvoid llhttp_free(llhttp_t* parser) {\n  free(parser);\n}\n\n#endif  // defined(__wasm__)\n\n/* Some getters required to get stuff from the parser */\n\nuint8_t llhttp_get_type(llhttp_t* parser) {\n  return parser->type;\n}\n\nuint8_t llhttp_get_http_major(llhttp_t* parser) {\n  return parser->http_major;\n}\n\nuint8_t llhttp_get_http_minor(llhttp_t* parser) {\n  return parser->http_minor;\n}\n\nuint8_t llhttp_get_method(llhttp_t* parser) {\n  return parser->method;\n}\n\nint llhttp_get_status_code(llhttp_t* parser) {\n  return parser->status_code;\n}\n\nuint8_t llhttp_get_upgrade(llhttp_t* parser) {\n  return parser->upgrade;\n}\n\n\nvoid llhttp_reset(llhttp_t* parser) {\n  llhttp_type_t type = parser->type;\n  const llhttp_settings_t* settings = parser->settings;\n  void* data = parser->data;\n  uint16_t lenient_flags = parser->lenient_flags;\n\n  llhttp__internal_init(parser);\n\n  parser->type = type;\n  parser->settings = (void*) settings;\n  parser->data = data;\n  parser->lenient_flags = lenient_flags;\n}\n\n\nllhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {\n  return llhttp__internal_execute(parser, data, data + len);\n}\n\n\nvoid llhttp_settings_init(llhttp_settings_t* settings) {\n  memset(settings, 0, sizeof(*settings));\n}\n\n\nllhttp_errno_t llhttp_finish(llhttp_t* parser) {\n  int err;\n\n  /* We're in an error state. Don't bother doing anything. */\n  if (parser->error != 0) {\n    return 0;\n  }\n\n  switch (parser->finish) {\n    case HTTP_FINISH_SAFE_WITH_CB:\n      CALLBACK_MAYBE(parser, on_message_complete);\n      if (err != HPE_OK) return err;\n\n    /* FALLTHROUGH */\n    case HTTP_FINISH_SAFE:\n      return HPE_OK;\n    case HTTP_FINISH_UNSAFE:\n      parser->reason = \"Invalid EOF state\";\n      return HPE_INVALID_EOF_STATE;\n    default:\n      abort();\n  }\n}\n\n\nvoid llhttp_pause(llhttp_t* parser) {\n  if (parser->error != HPE_OK) {\n    return;\n  }\n\n  parser->error = HPE_PAUSED;\n  parser->reason = \"Paused\";\n}\n\n\nvoid llhttp_resume(llhttp_t* parser) {\n  if (parser->error != HPE_PAUSED) {\n    return;\n  }\n\n  parser->error = 0;\n}\n\n\nvoid llhttp_resume_after_upgrade(llhttp_t* parser) {\n  if (parser->error != HPE_PAUSED_UPGRADE) {\n    return;\n  }\n\n  parser->error = 0;\n}\n\n\nllhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {\n  return parser->error;\n}\n\n\nconst char* llhttp_get_error_reason(const llhttp_t* parser) {\n  return parser->reason;\n}\n\n\nvoid llhttp_set_error_reason(llhttp_t* parser, const char* reason) {\n  parser->reason = reason;\n}\n\n\nconst char* llhttp_get_error_pos(const llhttp_t* parser) {\n  return parser->error_pos;\n}\n\n\nconst char* llhttp_errno_name(llhttp_errno_t err) {\n#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return \"HPE_\" #NAME;\n  switch (err) {\n    HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)\n    default: abort();\n  }\n#undef HTTP_ERRNO_GEN\n}\n\n\nconst char* llhttp_method_name(llhttp_method_t method) {\n#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;\n  switch (method) {\n    HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)\n    default: abort();\n  }\n#undef HTTP_METHOD_GEN\n}\n\nconst char* llhttp_status_name(llhttp_status_t status) {\n#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;\n  switch (status) {\n    HTTP_STATUS_MAP(HTTP_STATUS_GEN)\n    default: abort();\n  }\n#undef HTTP_STATUS_GEN\n}\n\n\nvoid llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_HEADERS;\n  } else {\n    parser->lenient_flags &= ~LENIENT_HEADERS;\n  }\n}\n\n\nvoid llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;\n  } else {\n    parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;\n  }\n}\n\n\nvoid llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_KEEP_ALIVE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;\n  }\n}\n\nvoid llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;\n  } else {\n    parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;\n  }\n}\n\nvoid llhttp_set_lenient_version(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_VERSION;\n  } else {\n    parser->lenient_flags &= ~LENIENT_VERSION;\n  }\n}\n\nvoid llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;\n  }\n}\n\nvoid llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;\n  }\n}\n\nvoid llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;\n  }\n}\n\nvoid llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;\n  }\n}\n\nvoid llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;\n  }\n}\n\n/* Callbacks */\n\n\nint llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_message_begin);\n  return err;\n}\n\n\nint llhttp__on_protocol(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_protocol_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_protocol_complete);\n  return err;\n}\n\n\nint llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_url_complete);\n  return err;\n}\n\n\nint llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_status_complete);\n  return err;\n}\n\n\nint llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_method_complete);\n  return err;\n}\n\n\nint llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_version_complete);\n  return err;\n}\n\n\nint llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_header_field_complete);\n  return err;\n}\n\n\nint llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_header_value_complete);\n  return err;\n}\n\n\nint llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_headers_complete);\n  return err;\n}\n\n\nint llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_message_complete);\n  return err;\n}\n\n\nint llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_header);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_extension_name_complete);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_extension_value_complete);\n  return err;\n}\n\n\nint llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_complete);\n  return err;\n}\n\n\nint llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_reset);\n  return err;\n}\n\n\n/* Private */\n\n\nvoid llhttp__debug(llhttp_t* s, const char* p, const char* endp,\n                   const char* msg) {\n  if (p == endp) {\n    fprintf(stderr, \"p=%p type=%d flags=%02x next=null debug=%s\\n\", s, s->type,\n            s->flags, msg);\n  } else {\n    fprintf(stderr, \"p=%p type=%d flags=%02x next=%02x   debug=%s\\n\", s,\n            s->type, s->flags, *p, msg);\n  }\n}\n"
  },
  {
    "path": "deps/llhttp/src/http.c",
    "content": "#include <stdio.h>\n#ifndef LLHTTP__TEST\n# include \"llhttp.h\"\n#else\n# define llhttp_t llparse_t\n#endif  /* */\n\nint llhttp_message_needs_eof(const llhttp_t* parser);\nint llhttp_should_keep_alive(const llhttp_t* parser);\n\nint llhttp__before_headers_complete(llhttp_t* parser, const char* p,\n                                    const char* endp) {\n  /* Set this here so that on_headers_complete() callbacks can see it */\n  if ((parser->flags & F_UPGRADE) &&\n      (parser->flags & F_CONNECTION_UPGRADE)) {\n    /* For responses, \"Upgrade: foo\" and \"Connection: upgrade\" are\n     * mandatory only when it is a 101 Switching Protocols response,\n     * otherwise it is purely informational, to announce support.\n     */\n    parser->upgrade =\n        (parser->type == HTTP_REQUEST || parser->status_code == 101);\n  } else {\n    parser->upgrade = (parser->method == HTTP_CONNECT);\n  }\n  return 0;\n}\n\n\n/* Return values:\n * 0 - No body, `restart`, message_complete\n * 1 - CONNECT request, `restart`, message_complete, and pause\n * 2 - chunk_size_start\n * 3 - body_identity\n * 4 - body_identity_eof\n * 5 - invalid transfer-encoding for request\n */\nint llhttp__after_headers_complete(llhttp_t* parser, const char* p,\n                                   const char* endp) {\n  int hasBody;\n\n  hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;\n  if (\n      (parser->upgrade && (parser->method == HTTP_CONNECT ||\n                          (parser->flags & F_SKIPBODY) || !hasBody)) ||\n      /* See RFC 2616 section 4.4 - 1xx e.g. Continue */\n      (parser->type == HTTP_RESPONSE && parser->status_code == 101)\n  ) {\n    /* Exit, the rest of the message is in a different protocol. */\n    return 1;\n  }\n\n  if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {\n    /* No body, restart as the message is complete */\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (\n    parser->flags & F_SKIPBODY ||         /* response to a HEAD request */\n    (\n      parser->type == HTTP_RESPONSE && (\n        parser->status_code == 102 ||     /* Processing */\n        parser->status_code == 103 ||     /* Early Hints */\n        parser->status_code == 204 ||     /* No Content */\n        parser->status_code == 304        /* Not Modified */\n      )\n    )\n  ) {\n    return 0;\n  } else if (parser->flags & F_CHUNKED) {\n    /* chunked encoding - ignore Content-Length header, prepare for a chunk */\n    return 2;\n  } else if (parser->flags & F_TRANSFER_ENCODING) {\n    if (parser->type == HTTP_REQUEST &&\n        (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&\n        (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {\n      /* RFC 7230 3.3.3 */\n\n      /* If a Transfer-Encoding header field\n       * is present in a request and the chunked transfer coding is not\n       * the final encoding, the message body length cannot be determined\n       * reliably; the server MUST respond with the 400 (Bad Request)\n       * status code and then close the connection.\n       */\n      return 5;\n    } else {\n      /* RFC 7230 3.3.3 */\n\n      /* If a Transfer-Encoding header field is present in a response and\n       * the chunked transfer coding is not the final encoding, the\n       * message body length is determined by reading the connection until\n       * it is closed by the server.\n       */\n      return 4;\n    }\n  } else {\n    if (!(parser->flags & F_CONTENT_LENGTH)) {\n      if (!llhttp_message_needs_eof(parser)) {\n        /* Assume content-length 0 - read the next */\n        return 0;\n      } else {\n        /* Read body until EOF */\n        return 4;\n      }\n    } else if (parser->content_length == 0) {\n      /* Content-Length header given but zero: Content-Length: 0\\r\\n */\n      return 0;\n    } else {\n      /* Content-Length header given and non-zero */\n      return 3;\n    }\n  }\n}\n\n\nint llhttp__after_message_complete(llhttp_t* parser, const char* p,\n                                   const char* endp) {\n  int should_keep_alive;\n\n  should_keep_alive = llhttp_should_keep_alive(parser);\n  parser->finish = HTTP_FINISH_SAFE;\n  parser->flags = 0;\n\n  /* NOTE: this is ignored in loose parsing mode */\n  return should_keep_alive;\n}\n\n\nint llhttp_message_needs_eof(const llhttp_t* parser) {\n  if (parser->type == HTTP_REQUEST) {\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */\n      parser->status_code == 204 ||     /* No Content */\n      parser->status_code == 304 ||     /* Not Modified */\n      (parser->flags & F_SKIPBODY)) {     /* response to a HEAD request */\n    return 0;\n  }\n\n  /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */\n  if ((parser->flags & F_TRANSFER_ENCODING) &&\n      (parser->flags & F_CHUNKED) == 0) {\n    return 1;\n  }\n\n  if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {\n    return 0;\n  }\n\n  return 1;\n}\n\n\nint llhttp_should_keep_alive(const llhttp_t* parser) {\n  if (parser->http_major > 0 && parser->http_minor > 0) {\n    /* HTTP/1.1 */\n    if (parser->flags & F_CONNECTION_CLOSE) {\n      return 0;\n    }\n  } else {\n    /* HTTP/1.0 or earlier */\n    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {\n      return 0;\n    }\n  }\n\n  return !llhttp_message_needs_eof(parser);\n}\n"
  },
  {
    "path": "deps/llhttp/src/llhttp.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n\n#ifdef __SSE4_2__\n #ifdef _MSC_VER\n  #include <nmmintrin.h>\n #else  /* !_MSC_VER */\n  #include <x86intrin.h>\n #endif  /* _MSC_VER */\n#endif  /* __SSE4_2__ */\n\n#ifdef __ARM_NEON__\n #include <arm_neon.h>\n#endif  /* __ARM_NEON__ */\n\n#ifdef __wasm__\n #include <wasm_simd128.h>\n#endif  /* __wasm__ */\n\n#ifdef _MSC_VER\n #define ALIGN(n) _declspec(align(n))\n #define UNREACHABLE __assume(0)\n#else  /* !_MSC_VER */\n #define ALIGN(n) __attribute__((aligned(n)))\n #define UNREACHABLE __builtin_unreachable()\n#endif  /* _MSC_VER */\n\n#include \"llhttp.h\"\n\ntypedef int (*llhttp__internal__span_cb)(\n             llhttp__internal_t*, const char*, const char*);\n\nstatic const unsigned char llparse_blob0[] = {\n  'o', 'n'\n};\nstatic const unsigned char llparse_blob1[] = {\n  'e', 'c', 't', 'i', 'o', 'n'\n};\nstatic const unsigned char llparse_blob2[] = {\n  'l', 'o', 's', 'e'\n};\nstatic const unsigned char llparse_blob3[] = {\n  'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e'\n};\nstatic const unsigned char llparse_blob4[] = {\n  'p', 'g', 'r', 'a', 'd', 'e'\n};\nstatic const unsigned char llparse_blob5[] = {\n  'c', 'h', 'u', 'n', 'k', 'e', 'd'\n};\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob6[] = {\n  0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,\n  0x0, 0x0, 0x0, 0x0, 0x0\n};\n#endif  /* __SSE4_2__ */\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob7[] = {\n  '!', '!', '#', '\\'', '*', '+', '-', '.', '0', '9', 'A',\n  'Z', '^', 'z', '|', '|'\n};\n#endif  /* __SSE4_2__ */\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob8[] = {\n  '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n  0x0, 0x0, 0x0, 0x0, 0x0\n};\n#endif  /* __SSE4_2__ */\nstatic const unsigned char llparse_blob9[] = {\n  'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h'\n};\nstatic const unsigned char llparse_blob10[] = {\n  'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c',\n  't', 'i', 'o', 'n'\n};\nstatic const unsigned char llparse_blob11[] = {\n  'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c',\n  'o', 'd', 'i', 'n', 'g'\n};\nstatic const unsigned char llparse_blob12[] = {\n  'p', 'g', 'r', 'a', 'd', 'e'\n};\nstatic const unsigned char llparse_blob13[] = {\n  'T', 'T', 'P'\n};\nstatic const unsigned char llparse_blob14[] = {\n  0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa\n};\nstatic const unsigned char llparse_blob15[] = {\n  'C', 'E'\n};\nstatic const unsigned char llparse_blob16[] = {\n  'T', 'S', 'P'\n};\nstatic const unsigned char llparse_blob17[] = {\n  'N', 'O', 'U', 'N', 'C', 'E'\n};\nstatic const unsigned char llparse_blob18[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob19[] = {\n  'E', 'C', 'K', 'O', 'U', 'T'\n};\nstatic const unsigned char llparse_blob20[] = {\n  'N', 'E', 'C', 'T'\n};\nstatic const unsigned char llparse_blob21[] = {\n  'E', 'T', 'E'\n};\nstatic const unsigned char llparse_blob22[] = {\n  'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob23[] = {\n  'L', 'U', 'S', 'H'\n};\nstatic const unsigned char llparse_blob24[] = {\n  'E', 'T'\n};\nstatic const unsigned char llparse_blob25[] = {\n  'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'\n};\nstatic const unsigned char llparse_blob26[] = {\n  'E', 'A', 'D'\n};\nstatic const unsigned char llparse_blob27[] = {\n  'N', 'K'\n};\nstatic const unsigned char llparse_blob28[] = {\n  'C', 'K'\n};\nstatic const unsigned char llparse_blob29[] = {\n  'S', 'E', 'A', 'R', 'C', 'H'\n};\nstatic const unsigned char llparse_blob30[] = {\n  'R', 'G', 'E'\n};\nstatic const unsigned char llparse_blob31[] = {\n  'C', 'T', 'I', 'V', 'I', 'T', 'Y'\n};\nstatic const unsigned char llparse_blob32[] = {\n  'L', 'E', 'N', 'D', 'A', 'R'\n};\nstatic const unsigned char llparse_blob33[] = {\n  'V', 'E'\n};\nstatic const unsigned char llparse_blob34[] = {\n  'O', 'T', 'I', 'F', 'Y'\n};\nstatic const unsigned char llparse_blob35[] = {\n  'P', 'T', 'I', 'O', 'N', 'S'\n};\nstatic const unsigned char llparse_blob36[] = {\n  'C', 'H'\n};\nstatic const unsigned char llparse_blob37[] = {\n  'S', 'E'\n};\nstatic const unsigned char llparse_blob38[] = {\n  'A', 'Y'\n};\nstatic const unsigned char llparse_blob39[] = {\n  'S', 'T'\n};\nstatic const unsigned char llparse_blob40[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob41[] = {\n  'A', 'T', 'C', 'H'\n};\nstatic const unsigned char llparse_blob42[] = {\n  'G', 'E'\n};\nstatic const unsigned char llparse_blob43[] = {\n  'U', 'E', 'R', 'Y'\n};\nstatic const unsigned char llparse_blob44[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob45[] = {\n  'O', 'R', 'D'\n};\nstatic const unsigned char llparse_blob46[] = {\n  'I', 'R', 'E', 'C', 'T'\n};\nstatic const unsigned char llparse_blob47[] = {\n  'O', 'R', 'T'\n};\nstatic const unsigned char llparse_blob48[] = {\n  'R', 'C', 'H'\n};\nstatic const unsigned char llparse_blob49[] = {\n  'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'\n};\nstatic const unsigned char llparse_blob50[] = {\n  'U', 'R', 'C', 'E'\n};\nstatic const unsigned char llparse_blob51[] = {\n  'B', 'S', 'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob52[] = {\n  'A', 'R', 'D', 'O', 'W', 'N'\n};\nstatic const unsigned char llparse_blob53[] = {\n  'A', 'C', 'E'\n};\nstatic const unsigned char llparse_blob54[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob55[] = {\n  'N', 'K'\n};\nstatic const unsigned char llparse_blob56[] = {\n  'C', 'K'\n};\nstatic const unsigned char llparse_blob57[] = {\n  'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob58[] = {\n  'T', 'T', 'P'\n};\nstatic const unsigned char llparse_blob59[] = {\n  'C', 'E'\n};\nstatic const unsigned char llparse_blob60[] = {\n  'T', 'S', 'P'\n};\nstatic const unsigned char llparse_blob61[] = {\n  'A', 'D'\n};\nstatic const unsigned char llparse_blob62[] = {\n  'T', 'P', '/'\n};\n\nenum llparse_match_status_e {\n  kMatchComplete,\n  kMatchPause,\n  kMatchMismatch\n};\ntypedef enum llparse_match_status_e llparse_match_status_t;\n\nstruct llparse_match_s {\n  llparse_match_status_t status;\n  const unsigned char* current;\n};\ntypedef struct llparse_match_s llparse_match_t;\n\nstatic llparse_match_t llparse__match_sequence_to_lower(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p));\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nstatic llparse_match_t llparse__match_sequence_to_lower_unsafe(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = ((*p) | 0x20);\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nstatic llparse_match_t llparse__match_sequence_id(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = *p;\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nenum llparse_state_e {\n  s_error,\n  s_n_llhttp__internal__n_closed,\n  s_n_llhttp__internal__n_invoke_llhttp__after_message_complete,\n  s_n_llhttp__internal__n_pause_1,\n  s_n_llhttp__internal__n_invoke_is_equal_upgrade,\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2,\n  s_n_llhttp__internal__n_chunk_data_almost_done_1,\n  s_n_llhttp__internal__n_chunk_data_almost_done,\n  s_n_llhttp__internal__n_consume_content_length,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body,\n  s_n_llhttp__internal__n_invoke_is_equal_content_length,\n  s_n_llhttp__internal__n_chunk_size_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_9,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_10,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value_done,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2,\n  s_n_llhttp__internal__n_error_30,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair,\n  s_n_llhttp__internal__n_error_31,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3,\n  s_n_llhttp__internal__n_error_33,\n  s_n_llhttp__internal__n_chunk_extension_value,\n  s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value,\n  s_n_llhttp__internal__n_error_34,\n  s_n_llhttp__internal__n_chunk_extension_name,\n  s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name,\n  s_n_llhttp__internal__n_chunk_extensions,\n  s_n_llhttp__internal__n_chunk_size_otherwise,\n  s_n_llhttp__internal__n_chunk_size,\n  s_n_llhttp__internal__n_chunk_size_digit,\n  s_n_llhttp__internal__n_invoke_update_content_length_1,\n  s_n_llhttp__internal__n_consume_content_length_1,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body_1,\n  s_n_llhttp__internal__n_eof,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body_2,\n  s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete,\n  s_n_llhttp__internal__n_error_5,\n  s_n_llhttp__internal__n_headers_almost_done,\n  s_n_llhttp__internal__n_header_field_colon_discard_ws,\n  s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_value,\n  s_n_llhttp__internal__n_header_value_discard_lws,\n  s_n_llhttp__internal__n_header_value_discard_ws_almost_done,\n  s_n_llhttp__internal__n_header_value_lws,\n  s_n_llhttp__internal__n_header_value_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_17,\n  s_n_llhttp__internal__n_header_value_lenient,\n  s_n_llhttp__internal__n_error_54,\n  s_n_llhttp__internal__n_header_value_otherwise,\n  s_n_llhttp__internal__n_header_value_connection_token,\n  s_n_llhttp__internal__n_header_value_connection_ws,\n  s_n_llhttp__internal__n_header_value_connection_1,\n  s_n_llhttp__internal__n_header_value_connection_2,\n  s_n_llhttp__internal__n_header_value_connection_3,\n  s_n_llhttp__internal__n_header_value_connection,\n  s_n_llhttp__internal__n_error_56,\n  s_n_llhttp__internal__n_error_57,\n  s_n_llhttp__internal__n_header_value_content_length_ws,\n  s_n_llhttp__internal__n_header_value_content_length,\n  s_n_llhttp__internal__n_error_59,\n  s_n_llhttp__internal__n_error_58,\n  s_n_llhttp__internal__n_header_value_te_token_ows,\n  s_n_llhttp__internal__n_header_value,\n  s_n_llhttp__internal__n_header_value_te_token,\n  s_n_llhttp__internal__n_header_value_te_chunked_last,\n  s_n_llhttp__internal__n_header_value_te_chunked,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1,\n  s_n_llhttp__internal__n_header_value_discard_ws,\n  s_n_llhttp__internal__n_invoke_load_header_state,\n  s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete,\n  s_n_llhttp__internal__n_header_field_general_otherwise,\n  s_n_llhttp__internal__n_header_field_general,\n  s_n_llhttp__internal__n_header_field_colon,\n  s_n_llhttp__internal__n_header_field_3,\n  s_n_llhttp__internal__n_header_field_4,\n  s_n_llhttp__internal__n_header_field_2,\n  s_n_llhttp__internal__n_header_field_1,\n  s_n_llhttp__internal__n_header_field_5,\n  s_n_llhttp__internal__n_header_field_6,\n  s_n_llhttp__internal__n_header_field_7,\n  s_n_llhttp__internal__n_header_field,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_field,\n  s_n_llhttp__internal__n_header_field_start,\n  s_n_llhttp__internal__n_headers_start,\n  s_n_llhttp__internal__n_url_to_http_09,\n  s_n_llhttp__internal__n_url_skip_to_http09,\n  s_n_llhttp__internal__n_url_skip_lf_to_http09_1,\n  s_n_llhttp__internal__n_url_skip_lf_to_http09,\n  s_n_llhttp__internal__n_req_pri_upgrade,\n  s_n_llhttp__internal__n_req_http_complete_crlf,\n  s_n_llhttp__internal__n_req_http_complete,\n  s_n_llhttp__internal__n_invoke_load_method_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_version_complete,\n  s_n_llhttp__internal__n_error_67,\n  s_n_llhttp__internal__n_error_74,\n  s_n_llhttp__internal__n_req_http_minor,\n  s_n_llhttp__internal__n_error_75,\n  s_n_llhttp__internal__n_req_http_dot,\n  s_n_llhttp__internal__n_error_76,\n  s_n_llhttp__internal__n_req_http_major,\n  s_n_llhttp__internal__n_span_start_llhttp__on_version,\n  s_n_llhttp__internal__n_req_after_protocol,\n  s_n_llhttp__internal__n_invoke_load_method,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete,\n  s_n_llhttp__internal__n_error_82,\n  s_n_llhttp__internal__n_req_after_http_start_1,\n  s_n_llhttp__internal__n_invoke_load_method_2,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1,\n  s_n_llhttp__internal__n_req_after_http_start_2,\n  s_n_llhttp__internal__n_invoke_load_method_3,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2,\n  s_n_llhttp__internal__n_req_after_http_start_3,\n  s_n_llhttp__internal__n_req_after_http_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_protocol,\n  s_n_llhttp__internal__n_req_http_start,\n  s_n_llhttp__internal__n_url_to_http,\n  s_n_llhttp__internal__n_url_skip_to_http,\n  s_n_llhttp__internal__n_url_fragment,\n  s_n_llhttp__internal__n_span_end_stub_query_3,\n  s_n_llhttp__internal__n_url_query,\n  s_n_llhttp__internal__n_url_query_or_fragment,\n  s_n_llhttp__internal__n_url_path,\n  s_n_llhttp__internal__n_span_start_stub_path_2,\n  s_n_llhttp__internal__n_span_start_stub_path,\n  s_n_llhttp__internal__n_span_start_stub_path_1,\n  s_n_llhttp__internal__n_url_server_with_at,\n  s_n_llhttp__internal__n_url_server,\n  s_n_llhttp__internal__n_url_schema_delim_1,\n  s_n_llhttp__internal__n_url_schema_delim,\n  s_n_llhttp__internal__n_span_end_stub_schema,\n  s_n_llhttp__internal__n_url_schema,\n  s_n_llhttp__internal__n_url_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_url_1,\n  s_n_llhttp__internal__n_url_entry_normal,\n  s_n_llhttp__internal__n_span_start_llhttp__on_url,\n  s_n_llhttp__internal__n_url_entry_connect,\n  s_n_llhttp__internal__n_req_spaces_before_url,\n  s_n_llhttp__internal__n_req_first_space_before_url,\n  s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1,\n  s_n_llhttp__internal__n_after_start_req_2,\n  s_n_llhttp__internal__n_after_start_req_3,\n  s_n_llhttp__internal__n_after_start_req_1,\n  s_n_llhttp__internal__n_after_start_req_4,\n  s_n_llhttp__internal__n_after_start_req_6,\n  s_n_llhttp__internal__n_after_start_req_8,\n  s_n_llhttp__internal__n_after_start_req_9,\n  s_n_llhttp__internal__n_after_start_req_7,\n  s_n_llhttp__internal__n_after_start_req_5,\n  s_n_llhttp__internal__n_after_start_req_12,\n  s_n_llhttp__internal__n_after_start_req_13,\n  s_n_llhttp__internal__n_after_start_req_11,\n  s_n_llhttp__internal__n_after_start_req_10,\n  s_n_llhttp__internal__n_after_start_req_14,\n  s_n_llhttp__internal__n_after_start_req_17,\n  s_n_llhttp__internal__n_after_start_req_16,\n  s_n_llhttp__internal__n_after_start_req_15,\n  s_n_llhttp__internal__n_after_start_req_18,\n  s_n_llhttp__internal__n_after_start_req_20,\n  s_n_llhttp__internal__n_after_start_req_21,\n  s_n_llhttp__internal__n_after_start_req_19,\n  s_n_llhttp__internal__n_after_start_req_23,\n  s_n_llhttp__internal__n_after_start_req_24,\n  s_n_llhttp__internal__n_after_start_req_26,\n  s_n_llhttp__internal__n_after_start_req_28,\n  s_n_llhttp__internal__n_after_start_req_29,\n  s_n_llhttp__internal__n_after_start_req_27,\n  s_n_llhttp__internal__n_after_start_req_25,\n  s_n_llhttp__internal__n_after_start_req_30,\n  s_n_llhttp__internal__n_after_start_req_22,\n  s_n_llhttp__internal__n_after_start_req_31,\n  s_n_llhttp__internal__n_after_start_req_32,\n  s_n_llhttp__internal__n_after_start_req_35,\n  s_n_llhttp__internal__n_after_start_req_36,\n  s_n_llhttp__internal__n_after_start_req_34,\n  s_n_llhttp__internal__n_after_start_req_37,\n  s_n_llhttp__internal__n_after_start_req_38,\n  s_n_llhttp__internal__n_after_start_req_42,\n  s_n_llhttp__internal__n_after_start_req_43,\n  s_n_llhttp__internal__n_after_start_req_41,\n  s_n_llhttp__internal__n_after_start_req_40,\n  s_n_llhttp__internal__n_after_start_req_39,\n  s_n_llhttp__internal__n_after_start_req_45,\n  s_n_llhttp__internal__n_after_start_req_44,\n  s_n_llhttp__internal__n_after_start_req_33,\n  s_n_llhttp__internal__n_after_start_req_46,\n  s_n_llhttp__internal__n_after_start_req_49,\n  s_n_llhttp__internal__n_after_start_req_50,\n  s_n_llhttp__internal__n_after_start_req_51,\n  s_n_llhttp__internal__n_after_start_req_52,\n  s_n_llhttp__internal__n_after_start_req_48,\n  s_n_llhttp__internal__n_after_start_req_47,\n  s_n_llhttp__internal__n_after_start_req_55,\n  s_n_llhttp__internal__n_after_start_req_57,\n  s_n_llhttp__internal__n_after_start_req_58,\n  s_n_llhttp__internal__n_after_start_req_56,\n  s_n_llhttp__internal__n_after_start_req_54,\n  s_n_llhttp__internal__n_after_start_req_59,\n  s_n_llhttp__internal__n_after_start_req_60,\n  s_n_llhttp__internal__n_after_start_req_53,\n  s_n_llhttp__internal__n_after_start_req_62,\n  s_n_llhttp__internal__n_after_start_req_63,\n  s_n_llhttp__internal__n_after_start_req_61,\n  s_n_llhttp__internal__n_after_start_req_66,\n  s_n_llhttp__internal__n_after_start_req_68,\n  s_n_llhttp__internal__n_after_start_req_69,\n  s_n_llhttp__internal__n_after_start_req_67,\n  s_n_llhttp__internal__n_after_start_req_70,\n  s_n_llhttp__internal__n_after_start_req_65,\n  s_n_llhttp__internal__n_after_start_req_64,\n  s_n_llhttp__internal__n_after_start_req,\n  s_n_llhttp__internal__n_span_start_llhttp__on_method_1,\n  s_n_llhttp__internal__n_res_line_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_30,\n  s_n_llhttp__internal__n_res_status,\n  s_n_llhttp__internal__n_span_start_llhttp__on_status,\n  s_n_llhttp__internal__n_res_status_code_otherwise,\n  s_n_llhttp__internal__n_res_status_code_digit_3,\n  s_n_llhttp__internal__n_res_status_code_digit_2,\n  s_n_llhttp__internal__n_res_status_code_digit_1,\n  s_n_llhttp__internal__n_res_after_version,\n  s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1,\n  s_n_llhttp__internal__n_error_93,\n  s_n_llhttp__internal__n_error_107,\n  s_n_llhttp__internal__n_res_http_minor,\n  s_n_llhttp__internal__n_error_108,\n  s_n_llhttp__internal__n_res_http_dot,\n  s_n_llhttp__internal__n_error_109,\n  s_n_llhttp__internal__n_res_http_major,\n  s_n_llhttp__internal__n_span_start_llhttp__on_version_1,\n  s_n_llhttp__internal__n_res_after_protocol,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3,\n  s_n_llhttp__internal__n_error_115,\n  s_n_llhttp__internal__n_res_after_start_1,\n  s_n_llhttp__internal__n_res_after_start_2,\n  s_n_llhttp__internal__n_res_after_start_3,\n  s_n_llhttp__internal__n_res_after_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_method_complete,\n  s_n_llhttp__internal__n_req_or_res_method_2,\n  s_n_llhttp__internal__n_invoke_update_type_1,\n  s_n_llhttp__internal__n_req_or_res_method_3,\n  s_n_llhttp__internal__n_req_or_res_method_1,\n  s_n_llhttp__internal__n_req_or_res_method,\n  s_n_llhttp__internal__n_span_start_llhttp__on_method,\n  s_n_llhttp__internal__n_start_req_or_res,\n  s_n_llhttp__internal__n_invoke_load_type,\n  s_n_llhttp__internal__n_invoke_update_finish,\n  s_n_llhttp__internal__n_start,\n};\ntypedef enum llparse_state_e llparse_state_t;\n\nint llhttp__on_method(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_url(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_protocol(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_version(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_header_field(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_header_value(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_body(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_name(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_value(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_status(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_initial_message_completed(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->initial_message_completed;\n}\n\nint llhttp__on_reset(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_finish(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 2;\n  return 0;\n}\n\nint llhttp__on_message_begin(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_type(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->type;\n}\n\nint llhttp__internal__c_store_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->method = match;\n  return 0;\n}\n\nint llhttp__on_method_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->method == 5;\n}\n\nint llhttp__internal__c_update_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->http_major = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->http_minor = 9;\n  return 0;\n}\n\nint llhttp__on_url_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_test_lenient_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 1) == 1;\n}\n\nint llhttp__internal__c_test_lenient_flags_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 256) == 256;\n}\n\nint llhttp__internal__c_test_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 128) == 128;\n}\n\nint llhttp__on_chunk_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_message_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_upgrade(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->upgrade == 1;\n}\n\nint llhttp__after_message_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->content_length = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_initial_message_completed(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->initial_message_completed = 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_finish_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 0;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_2(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 4) == 4;\n}\n\nint llhttp__internal__c_test_lenient_flags_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 32) == 32;\n}\n\nint llhttp__before_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__after_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_mul_add_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->content_length > 0xffffffffffffffffULL / 16) {\n    return 1;\n  }\n  \n  state->content_length *= 16;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->content_length > 0xffffffffffffffffULL - match) {\n      return 1;\n    }\n  } else {\n    if (state->content_length < 0ULL - match) {\n      return 1;\n    }\n  }\n  state->content_length += match;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_4(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 512) == 512;\n}\n\nint llhttp__on_chunk_header(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->content_length == 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 128) == 128;\n}\n\nint llhttp__internal__c_or_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 128;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 64) == 64;\n}\n\nint llhttp__on_chunk_extension_name_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_value_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_finish_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 1;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 64;\n  return 0;\n}\n\nint llhttp__internal__c_update_upgrade(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->upgrade = 1;\n  return 0;\n}\n\nint llhttp__internal__c_store_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->header_state = match;\n  return 0;\n}\n\nint llhttp__on_header_field_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->header_state;\n}\n\nint llhttp__internal__c_test_flags_4(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 512) == 512;\n}\n\nint llhttp__internal__c_test_lenient_flags_22(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 2) == 2;\n}\n\nint llhttp__internal__c_or_flags_5(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 1;\n  return 0;\n}\n\nint llhttp__on_header_value_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_or_flags_6(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 2;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 4;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 8;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 6;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_6(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 5;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 7;\n  return 0;\n}\n\nint llhttp__internal__c_test_flags_2(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 32) == 32;\n}\n\nint llhttp__internal__c_mul_add_content_length_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->content_length > 0xffffffffffffffffULL / 10) {\n    return 1;\n  }\n  \n  state->content_length *= 10;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->content_length > 0xffffffffffffffffULL - match) {\n      return 1;\n    }\n  } else {\n    if (state->content_length < 0ULL - match) {\n      return 1;\n    }\n  }\n  state->content_length += match;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_17(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 32;\n  return 0;\n}\n\nint llhttp__internal__c_test_flags_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 8) == 8;\n}\n\nint llhttp__internal__c_test_lenient_flags_20(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 8) == 8;\n}\n\nint llhttp__internal__c_or_flags_18(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 512;\n  return 0;\n}\n\nint llhttp__internal__c_and_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags &= -9;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 8;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_20(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 16;\n  return 0;\n}\n\nint llhttp__on_protocol_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->method;\n}\n\nint llhttp__internal__c_store_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->http_major = match;\n  return 0;\n}\n\nint llhttp__internal__c_store_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->http_minor = match;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_24(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 16) == 16;\n}\n\nint llhttp__on_version_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->http_major;\n}\n\nint llhttp__internal__c_load_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->http_minor;\n}\n\nint llhttp__internal__c_update_status_code(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->status_code = 0;\n  return 0;\n}\n\nint llhttp__internal__c_mul_add_status_code(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->status_code > 0xffff / 10) {\n    return 1;\n  }\n  \n  state->status_code *= 10;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->status_code > 0xffff - match) {\n      return 1;\n    }\n  } else {\n    if (state->status_code < 0 - match) {\n      return 1;\n    }\n  }\n  state->status_code += match;\n  return 0;\n}\n\nint llhttp__on_status_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_type(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->type = 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_type_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->type = 2;\n  return 0;\n}\n\nint llhttp__internal_init(llhttp__internal_t* state) {\n  memset(state, 0, sizeof(*state));\n  state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start;\n  return 0;\n}\n\nstatic llparse_state_t llhttp__internal__run(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  int match;\n  switch ((llparse_state_t) (intptr_t) state->_current) {\n    case s_n_llhttp__internal__n_closed:\n    s_n_llhttp__internal__n_closed: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_closed;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_closed;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_closed;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: {\n      switch (llhttp__after_message_complete(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_update_content_length;\n        default:\n          goto s_n_llhttp__internal__n_invoke_update_finish_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_pause_1:\n    s_n_llhttp__internal__n_pause_1: {\n      state->error = 0x16;\n      state->reason = \"Pause on CONNECT/Upgrade\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_is_equal_upgrade:\n    s_n_llhttp__internal__n_invoke_is_equal_upgrade: {\n      switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n        default:\n          goto s_n_llhttp__internal__n_pause_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: {\n      switch (llhttp__on_message_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_is_equal_upgrade;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_13;\n        default:\n          goto s_n_llhttp__internal__n_error_38;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_data_almost_done_1:\n    s_n_llhttp__internal__n_chunk_data_almost_done_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_data_almost_done_1;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_data_almost_done:\n    s_n_llhttp__internal__n_chunk_data_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_data_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_data_almost_done_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_consume_content_length:\n    s_n_llhttp__internal__n_consume_content_length: {\n      size_t avail;\n      uint64_t need;\n      \n      avail = endp - p;\n      need = state->content_length;\n      if (avail >= need) {\n        p += need;\n        state->content_length = 0;\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_body;\n      }\n      \n      state->content_length -= avail;\n      return s_n_llhttp__internal__n_consume_content_length;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_consume_content_length;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_is_equal_content_length:\n    s_n_llhttp__internal__n_invoke_is_equal_content_length: {\n      switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_body;\n        default:\n          goto s_n_llhttp__internal__n_invoke_or_flags;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_almost_done:\n    s_n_llhttp__internal__n_chunk_size_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_9:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_9: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_20;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_5;\n        default:\n          goto s_n_llhttp__internal__n_error_19;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_6;\n        default:\n          goto s_n_llhttp__internal__n_error_21;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_7;\n        default:\n          goto s_n_llhttp__internal__n_error_22;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_10:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_10: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_25;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_8;\n        default:\n          goto s_n_llhttp__internal__n_error_24;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_9;\n        default:\n          goto s_n_llhttp__internal__n_error_26;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value_done:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        }\n        case ';': {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_29;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_10;\n        default:\n          goto s_n_llhttp__internal__n_error_27;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_30:\n    s_n_llhttp__internal__n_error_30: {\n      state->error = 0x2;\n      state->reason = \"Invalid quoted-pair in chunk extensions quoted value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_31:\n    s_n_llhttp__internal__n_error_31: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions quoted value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_11;\n        default:\n          goto s_n_llhttp__internal__n_error_32;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_33:\n    s_n_llhttp__internal__n_error_33: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_value:\n    s_n_llhttp__internal__n_chunk_extension_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_value;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_value;\n        }\n        case 4: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        case 5: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value:\n    s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_chunk_extension_value;\n      goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_34:\n    s_n_llhttp__internal__n_error_34: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions name\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_name:\n    s_n_llhttp__internal__n_chunk_extension_name: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_name;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_name;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2;\n        }\n        case 5: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name:\n    s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_chunk_extension_name;\n      goto s_n_llhttp__internal__n_chunk_extension_name;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extensions:\n    s_n_llhttp__internal__n_chunk_extensions: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extensions;\n      }\n      switch (*p) {\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_error_17;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_error_18;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_otherwise:\n    s_n_llhttp__internal__n_chunk_size_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_otherwise;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;\n        }\n        case ';': {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_35;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size:\n    s_n_llhttp__internal__n_chunk_size: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'A': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'B': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'C': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'D': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'E': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'F': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'a': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'b': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'c': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'd': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'e': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'f': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_chunk_size_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_digit:\n    s_n_llhttp__internal__n_chunk_size_digit: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_digit;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'A': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'B': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'C': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'D': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'E': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'F': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'a': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'b': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'c': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'd': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'e': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'f': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_37;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_content_length_1:\n    s_n_llhttp__internal__n_invoke_update_content_length_1: {\n      switch (llhttp__internal__c_update_content_length(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_chunk_size_digit;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_consume_content_length_1:\n    s_n_llhttp__internal__n_consume_content_length_1: {\n      size_t avail;\n      uint64_t need;\n      \n      avail = endp - p;\n      need = state->content_length;\n      if (avail >= need) {\n        p += need;\n        state->content_length = 0;\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1;\n      }\n      \n      state->content_length -= avail;\n      return s_n_llhttp__internal__n_consume_content_length_1;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_consume_content_length_1;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_eof:\n    s_n_llhttp__internal__n_eof: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_eof;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_eof;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body_2:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body_2;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_eof;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: {\n      switch (llhttp__after_headers_complete(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1;\n        case 2:\n          goto s_n_llhttp__internal__n_invoke_update_content_length_1;\n        case 3:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1;\n        case 4:\n          goto s_n_llhttp__internal__n_invoke_update_finish_3;\n        case 5:\n          goto s_n_llhttp__internal__n_error_39;\n        default:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_5:\n    s_n_llhttp__internal__n_error_5: {\n      state->error = 0xa;\n      state->reason = \"Invalid header field char\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_headers_almost_done:\n    s_n_llhttp__internal__n_headers_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_headers_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_flags_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_colon_discard_ws:\n    s_n_llhttp__internal__n_header_field_colon_discard_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_colon_discard_ws;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_colon_discard_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_colon;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: {\n      switch (llhttp__on_header_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_header_field_start;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_18;\n        default:\n          goto s_n_llhttp__internal__n_error_48;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_value: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_value;\n      goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_lws:\n    s_n_llhttp__internal__n_header_value_discard_lws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_lws;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_header_state_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_ws_almost_done:\n    s_n_llhttp__internal__n_header_value_discard_ws_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_ws_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_lws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_lws:\n    s_n_llhttp__internal__n_header_value_lws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_lws;\n      }\n      switch (*p) {\n        case 9: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;\n        }\n        case ' ': {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_header_state_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_almost_done:\n    s_n_llhttp__internal__n_header_value_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_lws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_53;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_17:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_17: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_header_value_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_51;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_lenient:\n    s_n_llhttp__internal__n_header_value_lenient: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_lenient;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_lenient;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_54:\n    s_n_llhttp__internal__n_error_54: {\n      state->error = 0xa;\n      state->reason = \"Invalid header value char\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_otherwise:\n    s_n_llhttp__internal__n_header_value_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_otherwise;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_token:\n    s_n_llhttp__internal__n_header_value_connection_token: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_token;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_ws:\n    s_n_llhttp__internal__n_header_value_connection_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_ws;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_ws;\n        }\n        case ',': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_load_header_state_6;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_1:\n    s_n_llhttp__internal__n_header_value_connection_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_1;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_3;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_2:\n    s_n_llhttp__internal__n_header_value_connection_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_2;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_6;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_3:\n    s_n_llhttp__internal__n_header_value_connection_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_3;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_7;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection:\n    s_n_llhttp__internal__n_header_value_connection: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        case 'c': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_1;\n        }\n        case 'k': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_2;\n        }\n        case 'u': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_56:\n    s_n_llhttp__internal__n_error_56: {\n      state->error = 0xb;\n      state->reason = \"Content-Length overflow\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_57:\n    s_n_llhttp__internal__n_error_57: {\n      state->error = 0xb;\n      state->reason = \"Invalid character in Content-Length\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_content_length_ws:\n    s_n_llhttp__internal__n_header_value_content_length_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_content_length_ws;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_or_flags_17;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_invoke_or_flags_17;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_content_length_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_content_length:\n    s_n_llhttp__internal__n_header_value_content_length: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_content_length;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_content_length_ws;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_59:\n    s_n_llhttp__internal__n_error_59: {\n      state->error = 0xf;\n      state->reason = \"Invalid `Transfer-Encoding` header value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_58:\n    s_n_llhttp__internal__n_error_58: {\n      state->error = 0xf;\n      state->reason = \"Invalid `Transfer-Encoding` header value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_token_ows:\n    s_n_llhttp__internal__n_header_value_te_token_ows: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_token_ows;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_te_chunked;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value:\n    s_n_llhttp__internal__n_header_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #ifdef __SSE4_2__\n      if (endp - p >= 16) {\n        __m128i ranges;\n        __m128i input;\n        int match_len;\n      \n        /* Load input */\n        input = _mm_loadu_si128((__m128i const*) p);\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob6);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 6,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value;\n        }\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n      }\n      #endif  /* __SSE4_2__ */\n      #ifdef __ARM_NEON__\n      while (endp - p >= 16) {\n        uint8x16_t input;\n        uint8x16_t single;\n        uint8x16_t mask;\n        uint8x8_t narrow;\n        uint64_t match_mask;\n        int match_len;\n      \n        /* Load input */\n        input = vld1q_u8(p);\n        /* Find first character that does not match `ranges` */\n        single = vceqq_u8(input, vdupq_n_u8(0x9));\n        mask = single;\n        single = vandq_u16(\n          vcgeq_u8(input, vdupq_n_u8(' ')),\n          vcleq_u8(input, vdupq_n_u8('~'))\n        );\n        mask = vorrq_u16(mask, single);\n        single = vandq_u16(\n          vcgeq_u8(input, vdupq_n_u8(0x80)),\n          vcleq_u8(input, vdupq_n_u8(0xff))\n        );\n        mask = vorrq_u16(mask, single);\n        narrow = vshrn_n_u16(mask, 4);\n        match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0);\n        match_len = __builtin_ctzll(match_mask) >> 2;\n        if (match_len != 16) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        p += 16;\n      }\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #endif  /* __ARM_NEON__ */\n      #ifdef __wasm_simd128__\n      while (endp - p >= 16) {\n        v128_t input;\n        v128_t mask;\n        v128_t single;\n        int match_len;\n      \n        /* Load input */\n        input = wasm_v128_load(p);\n        /* Find first character that does not match `ranges` */\n        single = wasm_i8x16_eq(input, wasm_u8x16_const_splat(0x9));\n        mask = single;\n        single = wasm_v128_and(\n          wasm_i8x16_ge(input, wasm_u8x16_const_splat(' ')),\n          wasm_i8x16_le(input, wasm_u8x16_const_splat('~'))\n        );\n        mask = wasm_v128_or(mask, single);\n        single = wasm_v128_and(\n          wasm_i8x16_ge(input, wasm_u8x16_const_splat(0x80)),\n          wasm_i8x16_le(input, wasm_u8x16_const_splat(0xff))\n        );\n        mask = wasm_v128_or(mask, single);\n        match_len = __builtin_ctz(\n          ~wasm_i8x16_bitmask(mask)\n        );\n        if (match_len != 16) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        p += 16;\n      }\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #endif  /* __wasm_simd128__ */\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_token:\n    s_n_llhttp__internal__n_header_value_te_token: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_token;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_9;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_chunked_last:\n    s_n_llhttp__internal__n_header_value_te_chunked_last: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_chunked_last;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_8;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_8;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_chunked_last;\n        }\n        case ',': {\n          goto s_n_llhttp__internal__n_invoke_load_type_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_chunked:\n    s_n_llhttp__internal__n_header_value_te_chunked: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_chunked;\n      }\n      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_chunked_last;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_te_chunked;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_value;\n      goto s_n_llhttp__internal__n_invoke_load_header_state_3;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_ws:\n    s_n_llhttp__internal__n_header_value_discard_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_ws;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_header_state:\n    s_n_llhttp__internal__n_invoke_load_header_state: {\n      switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n        case 2:\n          goto s_n_llhttp__internal__n_invoke_test_flags_4;\n        case 3:\n          goto s_n_llhttp__internal__n_invoke_test_flags_5;\n        default:\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: {\n      switch (llhttp__on_header_field_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_header_state;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_19;\n        default:\n          goto s_n_llhttp__internal__n_error_45;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_general_otherwise:\n    s_n_llhttp__internal__n_header_field_general_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_general_otherwise;\n      }\n      switch (*p) {\n        case ':': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_62;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_general:\n    s_n_llhttp__internal__n_header_field_general: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_general;\n      }\n      #ifdef __SSE4_2__\n      if (endp - p >= 16) {\n        __m128i ranges;\n        __m128i input;\n        int match_len;\n      \n        /* Load input */\n        input = _mm_loadu_si128((__m128i const*) p);\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob7);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 16,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob8);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 2,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        goto s_n_llhttp__internal__n_header_field_general_otherwise;\n      }\n      #endif  /* __SSE4_2__ */\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_general_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_colon:\n    s_n_llhttp__internal__n_header_field_colon: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_colon;\n      }\n      switch (*p) {\n        case ' ': {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13;\n        }\n        case ':': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_10;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_3:\n    s_n_llhttp__internal__n_header_field_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_3;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_4:\n    s_n_llhttp__internal__n_header_field_4: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_4;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_4;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_2:\n    s_n_llhttp__internal__n_header_field_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_2;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 'n': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_3;\n        }\n        case 't': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_4;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_1:\n    s_n_llhttp__internal__n_header_field_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_1;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_2;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_5:\n    s_n_llhttp__internal__n_header_field_5: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_5;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_5;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_6:\n    s_n_llhttp__internal__n_header_field_6: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_6;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_6;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_7:\n    s_n_llhttp__internal__n_header_field_7: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_7;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_7;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field:\n    s_n_llhttp__internal__n_header_field: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 'c': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_1;\n        }\n        case 'p': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_5;\n        }\n        case 't': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_6;\n        }\n        case 'u': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_7;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_field:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_field: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_field;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_field;\n      goto s_n_llhttp__internal__n_header_field;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_start:\n    s_n_llhttp__internal__n_header_field_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_start;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_headers_almost_done;\n        }\n        case ':': {\n          goto s_n_llhttp__internal__n_error_44;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_headers_start:\n    s_n_llhttp__internal__n_headers_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_headers_start;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_start;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_to_http_09:\n    s_n_llhttp__internal__n_url_to_http_09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_to_http_09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_http_major;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_to_http09:\n    s_n_llhttp__internal__n_url_skip_to_http09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_to_http09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http_09;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_lf_to_http09_1:\n    s_n_llhttp__internal__n_url_skip_lf_to_http09_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_lf_to_http09_1;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http_09;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_63;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_lf_to_http09:\n    s_n_llhttp__internal__n_url_skip_lf_to_http09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_63;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_pri_upgrade:\n    s_n_llhttp__internal__n_req_pri_upgrade: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_pri_upgrade;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_error_72;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_pri_upgrade;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_73;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_complete_crlf:\n    s_n_llhttp__internal__n_req_http_complete_crlf: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_complete_crlf;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_headers_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_complete:\n    s_n_llhttp__internal__n_req_http_complete: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_complete;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_complete_crlf;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_71;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_1:\n    s_n_llhttp__internal__n_invoke_load_method_1: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 34:\n          goto s_n_llhttp__internal__n_req_pri_upgrade;\n        default:\n          goto s_n_llhttp__internal__n_req_http_complete;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: {\n      switch (llhttp__on_version_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_1;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_21;\n        default:\n          goto s_n_llhttp__internal__n_error_68;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_67:\n    s_n_llhttp__internal__n_error_67: {\n      state->error = 0x9;\n      state->reason = \"Invalid HTTP version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_74:\n    s_n_llhttp__internal__n_error_74: {\n      state->error = 0x9;\n      state->reason = \"Invalid minor version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_minor:\n    s_n_llhttp__internal__n_req_http_minor: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_minor;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_75:\n    s_n_llhttp__internal__n_error_75: {\n      state->error = 0x9;\n      state->reason = \"Expected dot\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_dot:\n    s_n_llhttp__internal__n_req_http_dot: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_dot;\n      }\n      switch (*p) {\n        case '.': {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_76:\n    s_n_llhttp__internal__n_error_76: {\n      state->error = 0x9;\n      state->reason = \"Invalid major version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_major:\n    s_n_llhttp__internal__n_req_http_major: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_major;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_version:\n    s_n_llhttp__internal__n_span_start_llhttp__on_version: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_version;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_version;\n      goto s_n_llhttp__internal__n_req_http_major;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_protocol:\n    s_n_llhttp__internal__n_req_after_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_protocol;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_77;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method:\n    s_n_llhttp__internal__n_invoke_load_method: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 1:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 2:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 3:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 4:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 5:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 6:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 7:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 8:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 9:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 10:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 11:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 12:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 13:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 14:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 15:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 16:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 17:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 18:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 19:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 20:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 21:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 22:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 23:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 24:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 25:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 26:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 27:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 28:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 29:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 30:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 31:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 32:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 33:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 34:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 46:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_66;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_22;\n        default:\n          goto s_n_llhttp__internal__n_error_65;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_82:\n    s_n_llhttp__internal__n_error_82: {\n      state->error = 0x8;\n      state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_1:\n    s_n_llhttp__internal__n_req_after_http_start_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_1;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_2:\n    s_n_llhttp__internal__n_invoke_load_method_2: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 33:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_79;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_2;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_23;\n        default:\n          goto s_n_llhttp__internal__n_error_78;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_2:\n    s_n_llhttp__internal__n_req_after_http_start_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_3:\n    s_n_llhttp__internal__n_invoke_load_method_3: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 3:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 6:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 35:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 36:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 37:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 38:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 39:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 40:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 41:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 42:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 43:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 44:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 45:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_81;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_3;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_24;\n        default:\n          goto s_n_llhttp__internal__n_error_80;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_3:\n    s_n_llhttp__internal__n_req_after_http_start_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start:\n    s_n_llhttp__internal__n_req_after_http_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_1;\n        }\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_2;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_protocol:\n    s_n_llhttp__internal__n_span_start_llhttp__on_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_protocol;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_protocol;\n      goto s_n_llhttp__internal__n_req_after_http_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_start:\n    s_n_llhttp__internal__n_req_http_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_start;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_to_http:\n    s_n_llhttp__internal__n_url_to_http: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_to_http;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_to_http:\n    s_n_llhttp__internal__n_url_skip_to_http: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_to_http;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_fragment:\n    s_n_llhttp__internal__n_url_fragment: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_fragment;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_fragment;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_83;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_end_stub_query_3:\n    s_n_llhttp__internal__n_span_end_stub_query_3: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_end_stub_query_3;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_fragment;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_query:\n    s_n_llhttp__internal__n_url_query: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_query;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_end_stub_query_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_84;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_query_or_fragment:\n    s_n_llhttp__internal__n_url_query_or_fragment: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_query_or_fragment;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4;\n        }\n        case ' ': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5;\n        }\n        case '#': {\n          p++;\n          goto s_n_llhttp__internal__n_url_fragment;\n        }\n        case '?': {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_85;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_path:\n    s_n_llhttp__internal__n_url_path: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_path;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_url_path;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_url_query_or_fragment;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path_2:\n    s_n_llhttp__internal__n_span_start_stub_path_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path_2;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path:\n    s_n_llhttp__internal__n_span_start_stub_path: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path_1:\n    s_n_llhttp__internal__n_span_start_stub_path_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path_1;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_server_with_at:\n    s_n_llhttp__internal__n_url_server_with_at: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,\n        8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,\n        0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_server_with_at;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_start_stub_path_1;\n        }\n        case 7: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 8: {\n          p++;\n          goto s_n_llhttp__internal__n_error_86;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_87;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_server:\n    s_n_llhttp__internal__n_url_server: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,\n        8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,\n        0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_server;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_start_stub_path;\n        }\n        case 7: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 8: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server_with_at;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_88;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema_delim_1:\n    s_n_llhttp__internal__n_url_schema_delim_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema_delim_1;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_89;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema_delim:\n    s_n_llhttp__internal__n_url_schema_delim: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema_delim;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_url_schema_delim_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_89;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_end_stub_schema:\n    s_n_llhttp__internal__n_span_end_stub_schema: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_end_stub_schema;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_schema_delim;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema:\n    s_n_llhttp__internal__n_url_schema: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_stub_schema;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_url_schema;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_90;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_start:\n    s_n_llhttp__internal__n_url_start: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_start;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_start_stub_path_2;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_url_schema;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_91;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_url_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_url_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_url_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_url;\n      goto s_n_llhttp__internal__n_url_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_entry_normal:\n    s_n_llhttp__internal__n_url_entry_normal: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_entry_normal;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_url:\n    s_n_llhttp__internal__n_span_start_llhttp__on_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_url;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_url;\n      goto s_n_llhttp__internal__n_url_server;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_entry_connect:\n    s_n_llhttp__internal__n_url_entry_connect: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_entry_connect;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_url;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_spaces_before_url:\n    s_n_llhttp__internal__n_req_spaces_before_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_spaces_before_url;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_spaces_before_url;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_is_equal_method;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_first_space_before_url:\n    s_n_llhttp__internal__n_req_first_space_before_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_first_space_before_url;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_spaces_before_url;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_92;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: {\n      switch (llhttp__on_method_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_first_space_before_url;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_29;\n        default:\n          goto s_n_llhttp__internal__n_error_111;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_2:\n    s_n_llhttp__internal__n_after_start_req_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_2;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          match = 19;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_3:\n    s_n_llhttp__internal__n_after_start_req_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 36;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_1:\n    s_n_llhttp__internal__n_after_start_req_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_1;\n      }\n      switch (*p) {\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_2;\n        }\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_4:\n    s_n_llhttp__internal__n_after_start_req_4: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_4;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 16;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_4;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_6:\n    s_n_llhttp__internal__n_after_start_req_6: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_6;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 22;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_6;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_8:\n    s_n_llhttp__internal__n_after_start_req_8: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_8;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_8;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_9:\n    s_n_llhttp__internal__n_after_start_req_9: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_9;\n      }\n      switch (*p) {\n        case 'Y': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_7:\n    s_n_llhttp__internal__n_after_start_req_7: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_7;\n      }\n      switch (*p) {\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_8;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_9;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_5:\n    s_n_llhttp__internal__n_after_start_req_5: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_5;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_6;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_7;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_12:\n    s_n_llhttp__internal__n_after_start_req_12: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_12;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_12;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_13:\n    s_n_llhttp__internal__n_after_start_req_13: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_13;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 35;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_13;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_11:\n    s_n_llhttp__internal__n_after_start_req_11: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_11;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_12;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_13;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_10:\n    s_n_llhttp__internal__n_after_start_req_10: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_10;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_11;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_14:\n    s_n_llhttp__internal__n_after_start_req_14: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_14;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 45;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_14;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_17:\n    s_n_llhttp__internal__n_after_start_req_17: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_17;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 41;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_17;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_16:\n    s_n_llhttp__internal__n_after_start_req_16: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_16;\n      }\n      switch (*p) {\n        case '_': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_17;\n        }\n        default: {\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_15:\n    s_n_llhttp__internal__n_after_start_req_15: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_15;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_16;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_15;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_18:\n    s_n_llhttp__internal__n_after_start_req_18: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_18;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_18;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_20:\n    s_n_llhttp__internal__n_after_start_req_20: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_20;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 31;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_20;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_21:\n    s_n_llhttp__internal__n_after_start_req_21: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_21;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_21;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_19:\n    s_n_llhttp__internal__n_after_start_req_19: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_19;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_20;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_21;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_23:\n    s_n_llhttp__internal__n_after_start_req_23: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_23;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 24;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_23;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_24:\n    s_n_llhttp__internal__n_after_start_req_24: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_24;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 23;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_24;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_26:\n    s_n_llhttp__internal__n_after_start_req_26: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_26;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 21;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_26;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_28:\n    s_n_llhttp__internal__n_after_start_req_28: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_28;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 30;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_28;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_29:\n    s_n_llhttp__internal__n_after_start_req_29: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_29;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_27:\n    s_n_llhttp__internal__n_after_start_req_27: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_27;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_28;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_29;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_25:\n    s_n_llhttp__internal__n_after_start_req_25: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_25;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_26;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_27;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_30:\n    s_n_llhttp__internal__n_after_start_req_30: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_30;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_30;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_22:\n    s_n_llhttp__internal__n_after_start_req_22: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_22;\n      }\n      switch (*p) {\n        case '-': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_23;\n        }\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_24;\n        }\n        case 'K': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_25;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_30;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_31:\n    s_n_llhttp__internal__n_after_start_req_31: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_31;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 25;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_31;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_32:\n    s_n_llhttp__internal__n_after_start_req_32: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_32;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_32;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_35:\n    s_n_llhttp__internal__n_after_start_req_35: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_35;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 28;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_35;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_36:\n    s_n_llhttp__internal__n_after_start_req_36: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_36;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 39;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_36;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_34:\n    s_n_llhttp__internal__n_after_start_req_34: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_34;\n      }\n      switch (*p) {\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_35;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_36;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_37:\n    s_n_llhttp__internal__n_after_start_req_37: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_37;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 38;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_37;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_38:\n    s_n_llhttp__internal__n_after_start_req_38: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_38;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_38;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_42:\n    s_n_llhttp__internal__n_after_start_req_42: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_42;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_42;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_43:\n    s_n_llhttp__internal__n_after_start_req_43: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_43;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_43;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_41:\n    s_n_llhttp__internal__n_after_start_req_41: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_41;\n      }\n      switch (*p) {\n        case 'F': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_42;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_43;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_40:\n    s_n_llhttp__internal__n_after_start_req_40: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_40;\n      }\n      switch (*p) {\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_41;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_39:\n    s_n_llhttp__internal__n_after_start_req_39: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_39;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          match = 34;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_40;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_45:\n    s_n_llhttp__internal__n_after_start_req_45: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_45;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 29;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_45;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_44:\n    s_n_llhttp__internal__n_after_start_req_44: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_44;\n      }\n      switch (*p) {\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_45;\n        }\n        case 'T': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_33:\n    s_n_llhttp__internal__n_after_start_req_33: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_33;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_34;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_37;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_38;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_39;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_44;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_46:\n    s_n_llhttp__internal__n_after_start_req_46: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_46;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 46;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_46;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_49:\n    s_n_llhttp__internal__n_after_start_req_49: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_49;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 17;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_49;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_50:\n    s_n_llhttp__internal__n_after_start_req_50: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_50;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 44;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_50;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_51:\n    s_n_llhttp__internal__n_after_start_req_51: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_51;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 43;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_51;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_52:\n    s_n_llhttp__internal__n_after_start_req_52: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_52;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 20;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_52;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_48:\n    s_n_llhttp__internal__n_after_start_req_48: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_48;\n      }\n      switch (*p) {\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_49;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_50;\n        }\n        case 'D': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_51;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_52;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_47:\n    s_n_llhttp__internal__n_after_start_req_47: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_47;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_48;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_55:\n    s_n_llhttp__internal__n_after_start_req_55: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_55;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_55;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_57:\n    s_n_llhttp__internal__n_after_start_req_57: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_57;\n      }\n      switch (*p) {\n        case 'P': {\n          p++;\n          match = 37;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_58:\n    s_n_llhttp__internal__n_after_start_req_58: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_58;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 42;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_58;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_56:\n    s_n_llhttp__internal__n_after_start_req_56: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_56;\n      }\n      switch (*p) {\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_57;\n        }\n        case '_': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_58;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_54:\n    s_n_llhttp__internal__n_after_start_req_54: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_54;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_55;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_56;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_59:\n    s_n_llhttp__internal__n_after_start_req_59: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_59;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 33;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_59;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_60:\n    s_n_llhttp__internal__n_after_start_req_60: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_60;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 26;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_60;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_53:\n    s_n_llhttp__internal__n_after_start_req_53: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_53;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_54;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_59;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_60;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_62:\n    s_n_llhttp__internal__n_after_start_req_62: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_62;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 40;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_62;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_63:\n    s_n_llhttp__internal__n_after_start_req_63: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_63;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_63;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_61:\n    s_n_llhttp__internal__n_after_start_req_61: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_61;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_62;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_63;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_66:\n    s_n_llhttp__internal__n_after_start_req_66: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_66;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 18;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_66;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_68:\n    s_n_llhttp__internal__n_after_start_req_68: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_68;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 32;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_68;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_69:\n    s_n_llhttp__internal__n_after_start_req_69: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_69;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_69;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_67:\n    s_n_llhttp__internal__n_after_start_req_67: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_67;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_68;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_69;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_70:\n    s_n_llhttp__internal__n_after_start_req_70: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_70;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 27;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_70;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_65:\n    s_n_llhttp__internal__n_after_start_req_65: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_65;\n      }\n      switch (*p) {\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_66;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_67;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_70;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_64:\n    s_n_llhttp__internal__n_after_start_req_64: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_64;\n      }\n      switch (*p) {\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_65;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req:\n    s_n_llhttp__internal__n_after_start_req: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_1;\n        }\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_4;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_5;\n        }\n        case 'D': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_10;\n        }\n        case 'F': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_14;\n        }\n        case 'G': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_15;\n        }\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_18;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_19;\n        }\n        case 'M': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_22;\n        }\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_31;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_32;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_33;\n        }\n        case 'Q': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_46;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_47;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_53;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_61;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_64;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_method_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_method_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_method;\n      goto s_n_llhttp__internal__n_after_start_req;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_line_almost_done:\n    s_n_llhttp__internal__n_res_line_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_line_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_30:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_30: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        default:\n          goto s_n_llhttp__internal__n_error_98;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status:\n    s_n_llhttp__internal__n_res_status: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_status;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_res_status;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_status:\n    s_n_llhttp__internal__n_span_start_llhttp__on_status: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_status;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_status;\n      goto s_n_llhttp__internal__n_res_status;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_otherwise:\n    s_n_llhttp__internal__n_res_status_code_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_otherwise;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_res_line_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_status;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_99;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_3:\n    s_n_llhttp__internal__n_res_status_code_digit_3: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_3;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_101;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_2:\n    s_n_llhttp__internal__n_res_status_code_digit_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_2;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_103;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_1:\n    s_n_llhttp__internal__n_res_status_code_digit_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_1;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_105;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_version:\n    s_n_llhttp__internal__n_res_after_version: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_version;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_status_code;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_106;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: {\n      switch (llhttp__on_version_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_res_after_version;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_28;\n        default:\n          goto s_n_llhttp__internal__n_error_94;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_93:\n    s_n_llhttp__internal__n_error_93: {\n      state->error = 0x9;\n      state->reason = \"Invalid HTTP version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_107:\n    s_n_llhttp__internal__n_error_107: {\n      state->error = 0x9;\n      state->reason = \"Invalid minor version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_minor:\n    s_n_llhttp__internal__n_res_http_minor: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_minor;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_108:\n    s_n_llhttp__internal__n_error_108: {\n      state->error = 0x9;\n      state->reason = \"Expected dot\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_dot:\n    s_n_llhttp__internal__n_res_http_dot: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_dot;\n      }\n      switch (*p) {\n        case '.': {\n          p++;\n          goto s_n_llhttp__internal__n_res_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_109:\n    s_n_llhttp__internal__n_error_109: {\n      state->error = 0x9;\n      state->reason = \"Invalid major version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_major:\n    s_n_llhttp__internal__n_res_http_major: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_major;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_version_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_version_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_version;\n      goto s_n_llhttp__internal__n_res_http_major;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_protocol:\n    s_n_llhttp__internal__n_res_after_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_protocol;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_114;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_res_after_protocol;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_30;\n        default:\n          goto s_n_llhttp__internal__n_error_113;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_115:\n    s_n_llhttp__internal__n_error_115: {\n      state->error = 0x8;\n      state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_1:\n    s_n_llhttp__internal__n_res_after_start_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_1;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_2:\n    s_n_llhttp__internal__n_res_after_start_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_3:\n    s_n_llhttp__internal__n_res_after_start_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start:\n    s_n_llhttp__internal__n_res_after_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_1;\n        }\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_2;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_protocol;\n      goto s_n_llhttp__internal__n_res_after_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: {\n      switch (llhttp__on_method_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_first_space_before_url;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_26;\n        default:\n          goto s_n_llhttp__internal__n_error_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_2:\n    s_n_llhttp__internal__n_req_or_res_method_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob61, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_method;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_or_res_method_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_type_1:\n    s_n_llhttp__internal__n_invoke_update_type_1: {\n      switch (llhttp__internal__c_update_type_1(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_3:\n    s_n_llhttp__internal__n_req_or_res_method_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob62, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_or_res_method_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_1:\n    s_n_llhttp__internal__n_req_or_res_method_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_1;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_2;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method:\n    s_n_llhttp__internal__n_req_or_res_method: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_method:\n    s_n_llhttp__internal__n_span_start_llhttp__on_method: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_method;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_method;\n      goto s_n_llhttp__internal__n_req_or_res_method;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_start_req_or_res:\n    s_n_llhttp__internal__n_start_req_or_res: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_start_req_or_res;\n      }\n      switch (*p) {\n        case 'H': {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_method;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_type_2;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_type:\n    s_n_llhttp__internal__n_invoke_load_type: {\n      switch (llhttp__internal__c_load_type(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n        case 2:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1;\n        default:\n          goto s_n_llhttp__internal__n_start_req_or_res;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_finish:\n    s_n_llhttp__internal__n_invoke_update_finish: {\n      switch (llhttp__internal__c_update_finish(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_start:\n    s_n_llhttp__internal__n_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_start;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_start;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_initial_message_completed;\n        }\n      }\n      UNREACHABLE;\n    }\n    default:\n      UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_2: {\n    state->error = 0x7;\n    state->reason = \"Invalid characters in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_2: {\n    switch (llhttp__internal__c_update_finish_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_start;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_initial_message_completed: {\n    switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_finish_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_content_length: {\n    switch (llhttp__internal__c_update_content_length(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_8: {\n    state->error = 0x5;\n    state->reason = \"Data after `Connection: close`\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_3: {\n    switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_closed;\n      default:\n        goto s_n_llhttp__internal__n_error_8;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_2: {\n    switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;\n      default:\n        goto s_n_llhttp__internal__n_closed;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_1: {\n    switch (llhttp__internal__c_update_finish_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_13: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_38: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_15: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_40: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_15;\n      default:\n        goto s_n_llhttp__internal__n_error_40;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_2: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_9: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: {\n    switch (llhttp__on_message_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_pause_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_2;\n      default:\n        goto s_n_llhttp__internal__n_error_9;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_36: {\n    state->error = 0xc;\n    state->reason = \"Chunk size overflow\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_10: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_4: {\n    switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_otherwise;\n      default:\n        goto s_n_llhttp__internal__n_error_10;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_3: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_14: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_update_content_length_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_3;\n      default:\n        goto s_n_llhttp__internal__n_error_14;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_13: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk data\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_6: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_13;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_15: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after chunk data\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_7: {\n    switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_15;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_body: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_body(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_chunk_data_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags: {\n    switch (llhttp__internal__c_or_flags(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_start;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_4: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_header pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_12: {\n    state->error = 0x13;\n    state->reason = \"`on_chunk_header` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: {\n    switch (llhttp__on_chunk_header(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_is_equal_content_length;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_4;\n      default:\n        goto s_n_llhttp__internal__n_error_12;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_16: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_8: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;\n      default:\n        goto s_n_llhttp__internal__n_error_16;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_11: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_5: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_almost_done;\n      default:\n        goto s_n_llhttp__internal__n_error_11;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_17: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_18: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_20: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension name\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_5: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_19: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_6: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_21: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_7: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_22: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_25: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_8: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_24: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_9: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_26: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_28: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_11: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_almost_done;\n      default:\n        goto s_n_llhttp__internal__n_error_28;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_29: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions quote value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_10: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_27: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_30;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_31;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_11: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_32: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_33;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_12: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_23: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: {\n    switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_chunk_extension_value;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_12;\n      default:\n        goto s_n_llhttp__internal__n_error_23;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_34;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_35: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_content_length: {\n    switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_36;\n      default:\n        goto s_n_llhttp__internal__n_chunk_size;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_37: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_body_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_body(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_3: {\n    switch (llhttp__internal__c_update_finish_3(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_39: {\n    state->error = 0xf;\n    state->reason = \"Request has invalid `Transfer-Encoding`\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_7: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: {\n    switch (llhttp__on_message_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n      case 21:\n        goto s_n_llhttp__internal__n_pause;\n      default:\n        goto s_n_llhttp__internal__n_error_7;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_1: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_2: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_upgrade: {\n    switch (llhttp__internal__c_update_upgrade(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_14: {\n    state->error = 0x15;\n    state->reason = \"Paused by on_headers_complete\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_6: {\n    state->error = 0x11;\n    state->reason = \"User callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: {\n    switch (llhttp__on_headers_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_or_flags_1;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_update_upgrade;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_14;\n      default:\n        goto s_n_llhttp__internal__n_error_6;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: {\n    switch (llhttp__before_headers_complete(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags: {\n    switch (llhttp__internal__c_test_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_1: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_flags;\n      default:\n        goto s_n_llhttp__internal__n_error_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_17: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_42: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_17;\n      default:\n        goto s_n_llhttp__internal__n_error_42;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_3: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_4: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_upgrade_1: {\n    switch (llhttp__internal__c_update_upgrade(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_16: {\n    state->error = 0x15;\n    state->reason = \"Paused by on_headers_complete\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_41: {\n    state->error = 0x11;\n    state->reason = \"User callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: {\n    switch (llhttp__on_headers_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_or_flags_3;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_update_upgrade_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_16;\n      default:\n        goto s_n_llhttp__internal__n_error_41;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: {\n    switch (llhttp__before_headers_complete(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_1: {\n    switch (llhttp__internal__c_test_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_43: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after headers\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_12: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_flags_1;\n      default:\n        goto s_n_llhttp__internal__n_error_43;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_44: {\n    state->error = 0xa;\n    state->reason = \"Invalid header token\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_5;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_13: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_field_colon_discard_ws;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_60: {\n    state->error = 0xb;\n    state->reason = \"Content-Length can't be present with Transfer-Encoding\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_47: {\n    state->error = 0xa;\n    state->reason = \"Invalid header value char\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_15: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n      default:\n        goto s_n_llhttp__internal__n_error_47;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_49: {\n    state->error = 0xb;\n    state->reason = \"Empty Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_18: {\n    state->error = 0x15;\n    state->reason = \"on_header_value_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_48: {\n    state->error = 0x1d;\n    state->reason = \"`on_header_value_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_5: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_6: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_7: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_8: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_2: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_5;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_6;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_7;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_8;\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_1: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 2:\n        goto s_n_llhttp__internal__n_error_49;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_46: {\n    state->error = 0xa;\n    state->reason = \"Invalid header value char\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_14: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_lws;\n      default:\n        goto s_n_llhttp__internal__n_error_46;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_50: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after CR\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_16: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_lws;\n      default:\n        goto s_n_llhttp__internal__n_error_50;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_1: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_4: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_1;\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_52: {\n    state->error = 0xa;\n    state->reason = \"Unexpected whitespace after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_18: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_header_state_4;\n      default:\n        goto s_n_llhttp__internal__n_error_52;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_2: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_9: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_10: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_11: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_12: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_5: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_9;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_10;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_11;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_12;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_53: {\n    state->error = 0x3;\n    state->reason = \"Missing expected LF after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_51: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_54;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_19: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_lenient;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_4: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_13: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_14: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_15: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_16: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_6: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_13;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_14;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_15;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_16;\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_5: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_token;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_3: {\n    switch (llhttp__internal__c_update_header_state_3(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_6: {\n    switch (llhttp__internal__c_update_header_state_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_7: {\n    switch (llhttp__internal__c_update_header_state_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_56;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_content_length_1: {\n    switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6;\n      default:\n        goto s_n_llhttp__internal__n_header_value_content_length;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_17: {\n    switch (llhttp__internal__c_or_flags_17(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_57;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_55: {\n    state->error = 0x4;\n    state->reason = \"Duplicate Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_2: {\n    switch (llhttp__internal__c_test_flags_2(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_header_value_content_length;\n      default:\n        goto s_n_llhttp__internal__n_error_55;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_59;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_8: {\n    switch (llhttp__internal__c_update_header_state_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_58;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_20: {\n    switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8;\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_type_1: {\n    switch (llhttp__internal__c_load_type(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20;\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_9: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_and_flags: {\n    switch (llhttp__internal__c_and_flags(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_19: {\n    switch (llhttp__internal__c_or_flags_18(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_and_flags;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_21: {\n    switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_19;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_type_2: {\n    switch (llhttp__internal__c_load_type(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_19;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_18: {\n    switch (llhttp__internal__c_or_flags_18(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_and_flags;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_3: {\n    switch (llhttp__internal__c_test_flags_3(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_type_2;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_18;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_20: {\n    switch (llhttp__internal__c_or_flags_20(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_9;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_3: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_connection;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_test_flags_2;\n      case 3:\n        goto s_n_llhttp__internal__n_invoke_test_flags_3;\n      case 4:\n        goto s_n_llhttp__internal__n_invoke_or_flags_20;\n      default:\n        goto s_n_llhttp__internal__n_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_22: {\n    switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_error_60;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_4: {\n    switch (llhttp__internal__c_test_flags_4(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_61: {\n    state->error = 0xf;\n    state->reason = \"Transfer-Encoding can't be present with Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_23: {\n    switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_error_61;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_5: {\n    switch (llhttp__internal__c_test_flags_2(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_19: {\n    state->error = 0x15;\n    state->reason = \"on_header_field_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_45: {\n    state->error = 0x1c;\n    state->reason = \"`on_header_field_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_62: {\n    state->error = 0xa;\n    state->reason = \"Invalid header token\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_10: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_general;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_header_state: {\n    switch (llhttp__internal__c_store_header_state(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_colon;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_11: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_general;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_4: {\n    state->error = 0x1e;\n    state->reason = \"Unexpected space after start line\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_field_start;\n      default:\n        goto s_n_llhttp__internal__n_error_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_20: {\n    state->error = 0x15;\n    state->reason = \"on_url_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_3: {\n    state->error = 0x1a;\n    state->reason = \"`on_url_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: {\n    switch (llhttp__on_url_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_headers_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_20;\n      default:\n        goto s_n_llhttp__internal__n_error_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_http_minor: {\n    switch (llhttp__internal__c_update_http_minor(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_http_major: {\n    switch (llhttp__internal__c_update_http_major(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_http_minor;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_63: {\n    state->error = 0x7;\n    state->reason = \"Expected CRLF\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_72: {\n    state->error = 0x17;\n    state->reason = \"Pause on PRI/Upgrade\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_73: {\n    state->error = 0x9;\n    state->reason = \"Expected HTTP/2 Connection Preface\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_70: {\n    state->error = 0x2;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_26: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_headers_start;\n      default:\n        goto s_n_llhttp__internal__n_error_70;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_69: {\n    state->error = 0x9;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_25: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_req_http_complete_crlf;\n      default:\n        goto s_n_llhttp__internal__n_error_69;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_71: {\n    state->error = 0x9;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_21: {\n    state->error = 0x15;\n    state->reason = \"on_version_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_68: {\n    state->error = 0x21;\n    state->reason = \"`on_version_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_67;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_67;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 9:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_1: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_2: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_major: {\n    switch (llhttp__internal__c_load_http_major(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_1;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_2;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_24: {\n    switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_http_major;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_minor: {\n    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_74;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_75;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_major: {\n    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_req_http_dot;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_76;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_77: {\n    state->error = 0x8;\n    state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_66: {\n    state->error = 0x8;\n    state->reason = \"Invalid method for HTTP/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_22: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_65: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_82;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_82;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_79: {\n    state->error = 0x8;\n    state->reason = \"Expected SOURCE method for ICE/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_23: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_78: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_81: {\n    state->error = 0x8;\n    state->reason = \"Invalid method for RTSP/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_24: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_3;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_80: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_25: {\n    state->error = 0x15;\n    state->reason = \"on_url_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_64: {\n    state->error = 0x1a;\n    state->reason = \"`on_url_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: {\n    switch (llhttp__on_url_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_req_http_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_25;\n      default:\n        goto s_n_llhttp__internal__n_error_64;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_83: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url fragment start\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_10: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_11: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_84: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url query\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_85: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url path\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_12: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_13: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_14: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_86: {\n    state->error = 0x7;\n    state->reason = \"Double @ in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_87: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url server\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_88: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url server\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_89: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url schema\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_90: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url schema\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_91: {\n    state->error = 0x7;\n    state->reason = \"Unexpected start char in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_is_equal_method: {\n    switch (llhttp__internal__c_is_equal_method(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_url_entry_normal;\n      default:\n        goto s_n_llhttp__internal__n_url_entry_connect;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_92: {\n    state->error = 0x6;\n    state->reason = \"Expected space after method\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_29: {\n    state->error = 0x15;\n    state->reason = \"on_method_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_111: {\n    state->error = 0x20;\n    state->reason = \"`on_method_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_method_1: {\n    switch (llhttp__internal__c_store_method(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_112: {\n    state->error = 0x6;\n    state->reason = \"Invalid method encountered\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_104: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_102: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_100: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_27: {\n    state->error = 0x15;\n    state->reason = \"on_status_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_96: {\n    state->error = 0x1b;\n    state->reason = \"`on_status_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: {\n    switch (llhttp__on_status_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_headers_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_27;\n      default:\n        goto s_n_llhttp__internal__n_error_96;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_95: {\n    state->error = 0xd;\n    state->reason = \"Invalid response status\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_28: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_95;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_97: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after CR\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_29: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_97;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_98: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after response line\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_status: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_status(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_status_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_status(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_res_line_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_99: {\n    state->error = 0xd;\n    state->reason = \"Invalid response status\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code_2: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_100;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_101: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code_1: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_102;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_103: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_104;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_105: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_status_code: {\n    switch (llhttp__internal__c_update_status_code(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_106: {\n    state->error = 0x9;\n    state->reason = \"Expected space after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_28: {\n    state->error = 0x15;\n    state->reason = \"on_version_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_94: {\n    state->error = 0x21;\n    state->reason = \"`on_version_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_93;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_93;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_3: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 9:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_4: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_5: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_major_1: {\n    switch (llhttp__internal__c_load_http_major(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_3;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_4;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_5;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_27: {\n    switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_http_major_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_minor_1: {\n    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_107;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_107;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_108;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_major_1: {\n    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_res_http_dot;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_109;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_114: {\n    state->error = 0x8;\n    state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_30: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_protocol;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_113: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_115;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_115;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_26: {\n    state->error = 0x15;\n    state->reason = \"on_method_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_1: {\n    state->error = 0x20;\n    state->reason = \"`on_method_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_type: {\n    switch (llhttp__internal__c_update_type(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_method;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_method: {\n    switch (llhttp__internal__c_store_method(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_type;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_110: {\n    state->error = 0x8;\n    state->reason = \"Invalid word encountered\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_update_type_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_type_2: {\n    switch (llhttp__internal__c_update_type(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_31: {\n    state->error = 0x15;\n    state->reason = \"on_message_begin pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error: {\n    state->error = 0x10;\n    state->reason = \"`on_message_begin` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: {\n    switch (llhttp__on_message_begin(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_type;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_31;\n      default:\n        goto s_n_llhttp__internal__n_error;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_32: {\n    state->error = 0x15;\n    state->reason = \"on_reset pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_116: {\n    state->error = 0x1f;\n    state->reason = \"`on_reset` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_reset: {\n    switch (llhttp__on_reset(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_update_finish;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_32;\n      default:\n        goto s_n_llhttp__internal__n_error_116;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_initial_message_completed: {\n    switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_reset;\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_finish;\n    }\n    UNREACHABLE;\n  }\n}\n\nint llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) {\n  llparse_state_t next;\n\n  /* check lingering errors */\n  if (state->error != 0) {\n    return state->error;\n  }\n\n  /* restart spans */\n  if (state->_span_pos0 != NULL) {\n    state->_span_pos0 = (void*) p;\n  }\n  \n  next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);\n  if (next == s_error) {\n    return state->error;\n  }\n  state->_current = (void*) (intptr_t) next;\n\n  /* execute spans */\n  if (state->_span_pos0 != NULL) {\n    int error;\n  \n    error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp);\n    if (error != 0) {\n      state->error = error;\n      state->error_pos = endp;\n      return error;\n    }\n  }\n  \n  return 0;\n}"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/CNAME",
    "content": "undici.nodejs.org"
  },
  {
    "path": "docs/docs/api/Agent.md",
    "content": "# Agent\n\nExtends: `undici.Dispatcher`\n\nAgent allows dispatching requests against multiple different origins.\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## `new undici.Agent([options])`\n\nArguments:\n\n* **options** `AgentOptions` (optional)\n\nReturns: `Agent`\n\n### Parameter: `AgentOptions`\n\nExtends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)\n\n* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`\n* **maxOrigins** `number` (optional) - Default: `Infinity` - Limits the total number of origins that can receive requests at a time, throwing an `MaxOriginsReachedError` error when attempting to dispatch when the max is reached. If `Infinity`, no limit is enforced.\n\n## Instance Properties\n\n### `Agent.closed`\n\nImplements [Client.closed](/docs/docs/api/Client.md#clientclosed)\n\n### `Agent.destroyed`\n\nImplements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)\n\n## Instance Methods\n\n### `Agent.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `Agent.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `Agent.dispatch(options, handler: AgentDispatchOptions)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n#### Parameter: `AgentDispatchOptions`\n\nExtends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions)\n\n* **origin** `string | URL`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `Agent.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `Agent.dispatch(options, handler)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `Agent.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `Agent.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `Agent.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `Agent.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n### `Agent.stats()`\n\nReturns an object of stats by origin in the format of `Record<string, TClientStats | TPoolStats>`\n\nSee [`PoolStats`](/docs/docs/api/PoolStats.md) and [`ClientStats`](/docs/docs/api/ClientStats.md).\n"
  },
  {
    "path": "docs/docs/api/BalancedPool.md",
    "content": "# Class: BalancedPool\n\nExtends: `undici.Dispatcher`\n\nA pool of [Pool](/docs/docs/api/Pool.md) instances connected to multiple upstreams.\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## `new BalancedPool(upstreams [, options])`\n\nArguments:\n\n* **upstreams** `URL | string | string[]` - It should only include the **protocol, hostname, and port**.\n* **options** `BalancedPoolOptions` (optional)\n\n### Parameter: `BalancedPoolOptions`\n\nExtends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)\n\n* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`\n\nThe `PoolOptions` are passed to each of the `Pool` instances being created.\n## Instance Properties\n\n### `BalancedPool.upstreams`\n\nReturns an array of upstreams that were previously added.\n\n### `BalancedPool.closed`\n\nImplements [Client.closed](/docs/docs/api/Client.md#clientclosed)\n\n### `BalancedPool.destroyed`\n\nImplements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)\n\n### `Pool.stats`\n\nReturns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.\n\n## Instance Methods\n\n### `BalancedPool.addUpstream(upstream)`\n\nAdd an upstream.\n\nArguments:\n\n* **upstream** `string` - It should only include the **protocol, hostname, and port**.\n\n### `BalancedPool.removeUpstream(upstream)`\n\nRemoves an upstream that was previously added.\n\n### `BalancedPool.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `BalancedPool.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `BalancedPool.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `BalancedPool.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `BalancedPool.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `BalancedPool.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `BalancedPool.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `BalancedPool.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n## Instance Events\n\n### Event: `'connect'`\n\nSee [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).\n\n### Event: `'disconnect'`\n\nSee [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).\n\n### Event: `'drain'`\n\nSee [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).\n"
  },
  {
    "path": "docs/docs/api/CacheStorage.md",
    "content": "# CacheStorage\n\nUndici exposes a W3C spec-compliant implementation of [CacheStorage](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage) and [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache).\n\n## Opening a Cache\n\nUndici exports a top-level CacheStorage instance. You can open a new Cache, or duplicate a Cache with an existing name, by using `CacheStorage.prototype.open`. If you open a Cache with the same name as an already-existing Cache, its list of cached Responses will be shared between both instances.\n\n```mjs\nimport { caches } from 'undici'\n\nconst cache_1 = await caches.open('v1')\nconst cache_2 = await caches.open('v1')\n\n// Although .open() creates a new instance,\nassert(cache_1 !== cache_2)\n// The same Response is matched in both.\nassert.deepStrictEqual(await cache_1.match('/req'), await cache_2.match('/req'))\n```\n\n## Deleting a Cache\n\nIf a Cache is deleted, the cached Responses/Requests can still be used.\n\n```mjs\nconst response = await cache_1.match('/req')\nawait caches.delete('v1')\n\nawait response.text() // the Response's body\n```\n"
  },
  {
    "path": "docs/docs/api/CacheStore.md",
    "content": "# Cache Store\n\nA Cache Store is responsible for storing and retrieving cached responses.\nIt is also responsible for deciding which specific response to use based off of\na response's `Vary` header (if present). It is expected to be compliant with\n[RFC-9111](https://www.rfc-editor.org/rfc/rfc9111.html).\n\n## Pre-built Cache Stores\n\n### `MemoryCacheStore`\n\nThe `MemoryCacheStore` stores the responses in-memory.\n\n**Options**\n\n- `maxSize` - The maximum total size in bytes of all stored responses. Default `104857600` (100MB).\n- `maxCount` - The maximum amount of responses to store. Default `1024`.\n- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `5242880` (5MB).\n\n### Getters\n\n#### `MemoryCacheStore.size`\n\nReturns the current total size in bytes of all stored responses.\n\n### Methods\n\n#### `MemoryCacheStore.isFull()`\n\nReturns a boolean indicating whether the cache has reached its maximum size or count.\n\n### Events\n\n#### `'maxSizeExceeded'`\n\nEmitted when the cache exceeds its maximum size or count limits. The event payload contains `size`, `maxSize`, `count`, and `maxCount` properties.\n\n\n### `SqliteCacheStore`\n\nThe `SqliteCacheStore` stores the responses in a SQLite database.\nUnder the hood, it uses Node.js' [`node:sqlite`](https://nodejs.org/api/sqlite.html) api.\nThe `SqliteCacheStore` is only exposed if the `node:sqlite` api is present.\n\n**Options**\n\n- `location` - The location of the SQLite database to use. Default `:memory:`.\n- `maxCount` - The maximum number of entries to store in the database. Default `Infinity`.\n- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `Infinity`.\n\n## Defining a Custom Cache Store\n\nThe store must implement the following functions:\n\n### Getter: `isFull`\n\nOptional. This tells the cache interceptor if the store is full or not. If this is true,\nthe cache interceptor will not attempt to cache the response.\n\n### Function: `get`\n\nParameters:\n\n* **req** `Dispatcher.RequestOptions` - Incoming request\n\nReturns: `GetResult | Promise<GetResult | undefined> | undefined` - If the request is cached, the cached response is returned. If the request's method is anything other than HEAD, the response is also returned.\nIf the request isn't cached, `undefined` is returned.\n\nThe `get` method may return a `Promise` for async cache stores (e.g. Redis-backed or remote stores). The cache interceptor handles both synchronous and asynchronous return values, including in revalidation paths (304 Not Modified handling and stale-while-revalidate background revalidation).\n\nResponse properties:\n\n* **response** `CacheValue` - The cached response data.\n* **body** `Readable | Iterable<Buffer> | undefined` - The response's body. This can be an array of `Buffer` chunks (with a `.values()` method) or a `Readable` stream. Both formats are supported in all code paths, including 304 revalidation.\n\n### Function: `createWriteStream`\n\nParameters:\n\n* **req** `Dispatcher.RequestOptions` - Incoming request\n* **value** `CacheValue` - Response to store\n\nReturns: `Writable | undefined` - If the store is full, return `undefined`. Otherwise, return a writable so that the cache interceptor can stream the body and trailers to the store.\n\n## `CacheValue`\n\nThis is an interface containing the majority of a response's data (minus the body).\n\n### Property `statusCode`\n\n`number` - The response's HTTP status code.\n\n### Property `statusMessage`\n\n`string` - The response's HTTP status message.\n\n### Property `rawHeaders`\n\n`Buffer[]` - The response's headers.\n\n### Property `vary`\n\n`Record<string, string | string[] | null> | undefined` - The headers defined by the response's `Vary` header\nand their respective values for later comparison. Values are `null` when the\nheader specified in `Vary` was not present in the original request. These `null`\nvalues are automatically filtered out during revalidation so they are not sent\nas request headers.\n\nFor example, for a response like\n```\nVary: content-encoding, accepts\ncontent-encoding: utf8\naccepts: application/json\n```\n\nThis would be\n```js\n{\n  'content-encoding': 'utf8',\n  accepts: 'application/json'\n}\n```\n\nIf the original request did not include the `accepts` header:\n```js\n{\n  'content-encoding': 'utf8',\n  accepts: null\n}\n```\n\n### Property `cachedAt`\n\n`number` - Time in millis that this value was cached.\n\n### Property `staleAt`\n\n`number` - Time in millis that this value is considered stale.\n\n### Property `deleteAt`\n\n`number` - Time in millis that this value is to be deleted from the cache. This\nis either the same sa staleAt or the `max-stale` caching directive.\n\nThe store must not return a response after the time defined in this property.\n\n## `CacheStoreReadable`\n\nThis extends Node's [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable)\nand defines extra properties relevant to the cache interceptor.\n\n### Getter: `value`\n\nThe response's [`CacheStoreValue`](/docs/docs/api/CacheStore.md#cachestorevalue)\n\n## `CacheStoreWriteable`\n\nThis extends Node's [`Writable`](https://nodejs.org/api/stream.html#class-streamwritable)\nand defines extra properties relevant to the cache interceptor.\n\n### Setter: `rawTrailers`\n\nIf the response has trailers, the cache interceptor will pass them to the cache\ninterceptor through this method.\n"
  },
  {
    "path": "docs/docs/api/Client.md",
    "content": "# Class: Client\n\nExtends: `undici.Dispatcher`\n\nA basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## `new Client(url[, options])`\n\nArguments:\n\n* **url** `URL | string` - Should only include the **protocol, hostname, and port**.\n* **options** `ClientOptions` (optional)\n\nReturns: `Client`\n\n### Parameter: `ClientOptions`\n\n* **bodyTimeout** `number | null` (optional) - Default: `300e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds. Please note the `timeout` will be reset if you keep writing data to the socket everytime.\n* **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.\n* **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.\n* **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.\n* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.\n* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.\n* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.\n* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.\n* **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.\n* **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source.\n* **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.\n* **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.\n* **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.\n* **useH2c**: `boolean` - Default: `false`. Enforces h2c for non-https connections.\n* **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.\n* **initialWindowSize**: `number` (optional) - Default: `262144` (256KB). Sets the HTTP/2 stream-level flow-control window size (SETTINGS_INITIAL_WINDOW_SIZE). Must be a positive integer greater than 0. This default is higher than Node.js core's default (65535 bytes) to improve throughput, Node's choice is very conservative for current high-bandwith networks. See [RFC 7540 Section 6.9.2](https://datatracker.ietf.org/doc/html/rfc7540#section-6.9.2) for more details.\n* **connectionWindowSize**: `number` (optional) - Default `524288` (512KB). Sets the HTTP/2 connection-level flow-control window size using `ClientHttp2Session.setLocalWindowSize()`. Must be a positive integer greater than 0. This provides better flow control for the entire connection across multiple streams. See [Node.js HTTP/2 documentation](https://nodejs.org/api/http2.html#clienthttp2sessionsetlocalwindowsize) for more details.\n* **pingInterval**: `number` - Default: `60e3`. The time interval in milliseconds between PING frames sent to the server. Set to `0` to disable PING frames. This is only applicable for HTTP/2 connections. This will emit a `ping` event on the client with the duration of the ping in milliseconds.\n\n> **Notes about HTTP/2**\n> - It only works under TLS connections. h2c is not supported.\n> - The server must support HTTP/2 and choose it as the protocol during the ALPN negotiation.\n>   - The server must not have a bigger priority for HTTP/1.1 than HTTP/2.\n> - Pseudo headers are automatically attached to the request. If you try to set them, they will be overwritten.\n>   - The `:path` header is automatically set to the request path.\n>   - The `:method` header is automatically set to the request method.\n>   - The `:scheme` header is automatically set to the request scheme.\n>   - The `:authority` header is automatically set to the request `host[:port]`.\n> - `PUSH` frames are yet not supported.\n\n#### Parameter: `ConnectOptions`\n\nEvery Tls option, see [here](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).\nFurthermore, the following options can be passed:\n\n* **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.\n* **maxCachedSessions** `number | null` (optional) - Default: `100` - Maximum number of TLS cached sessions. Use 0 to disable TLS session caching. Default: 100.\n* **timeout** `number | null` (optional) -  In milliseconds, Default `10e3`.\n* **servername** `string | null` (optional)\n* **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled\n* **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds\n\n### Example - Basic Client instantiation\n\nThis will instantiate the undici Client, but it will not connect to the origin until something is queued. Consider using `client.connect` to prematurely connect to the origin, or just call `client.request`.\n\n```js\n'use strict'\nimport { Client } from 'undici'\n\nconst client = new Client('http://localhost:3000')\n```\n\n### Example - Custom connector\n\nThis will allow you to perform some additional check on the socket that will be used for the next request.\n\n```js\n'use strict'\nimport { Client, buildConnector } from 'undici'\n\nconst connector = buildConnector({ rejectUnauthorized: false })\nconst client = new Client('https://localhost:3000', {\n  connect (opts, cb) {\n    connector(opts, (err, socket) => {\n      if (err) {\n        cb(err)\n      } else if (/* assertion */) {\n        socket.destroy()\n        cb(new Error('kaboom'))\n      } else {\n        cb(null, socket)\n      }\n    })\n  }\n})\n```\n\n## Instance Methods\n\n### `Client.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `Client.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\nWaits until socket is closed before invoking the callback (or returning a promise if no callback is provided).\n\n### `Client.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `Client.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `Client.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `Client.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `Client.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `Client.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n## Instance Properties\n\n### `Client.closed`\n\n* `boolean`\n\n`true` after `client.close()` has been called.\n\n### `Client.destroyed`\n\n* `boolean`\n\n`true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed.\n\n### `Client.pipelining`\n\n* `number`\n\nProperty to get and set the pipelining factor.\n\n## Instance Events\n\n### Event: `'connect'`\n\nSee [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).\n\nParameters:\n\n* **origin** `URL`\n* **targets** `Array<Dispatcher>`\n\nEmitted when a socket has been created and connected. The client will connect once `client.size > 0`.\n\n#### Example - Client connect event\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nclient.on('connect', (origin) => {\n  console.log(`Connected to ${origin}`) // should print before the request body statement\n})\n\ntry {\n  const { body } = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n  body.setEncoding('utf-8')\n  body.on('data', console.log)\n  client.close()\n  server.close()\n} catch (error) {\n  console.error(error)\n  client.close()\n  server.close()\n}\n```\n\n### Event: `'disconnect'`\n\nSee [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).\n\nParameters:\n\n* **origin** `URL`\n* **targets** `Array<Dispatcher>`\n* **error** `Error`\n\nEmitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`.\n\n#### Example - Client disconnect event\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.destroy()\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nclient.on('disconnect', (origin) => {\n  console.log(`Disconnected from ${origin}`)\n})\n\ntry {\n  await client.request({\n    path: '/',\n    method: 'GET'\n  })\n} catch (error) {\n  console.error(error.message)\n  client.close()\n  server.close()\n}\n```\n\n### Event: `'drain'`\n\nEmitted when pipeline is no longer busy.\n\nSee [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).\n\n#### Example - Client drain event\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nclient.on('drain', () => {\n  console.log('drain event')\n  client.close()\n  server.close()\n})\n\nconst requests = [\n  client.request({ path: '/', method: 'GET' }),\n  client.request({ path: '/', method: 'GET' }),\n  client.request({ path: '/', method: 'GET' })\n]\n\nawait Promise.all(requests)\n\nconsole.log('requests completed')\n```\n\n### Event: `'error'`\n\nInvoked for users errors such as throwing in the `onError` handler.\n"
  },
  {
    "path": "docs/docs/api/ClientStats.md",
    "content": "# Class: ClientStats\n\nStats for a [Client](/docs/docs/api/Client.md).\n\n## `new ClientStats(client)`\n\nArguments:\n\n* **client** `Client` - Client from which to return stats.\n\n## Instance Properties\n\n### `ClientStats.connected`\n\nBoolean if socket as open connection by this client.\n\n### `ClientStats.pending`\n\nNumber of pending requests of this client.\n\n### `ClientStats.running`\n\nNumber of currently active requests across this client.\n\n### `ClientStats.size`\n\nNumber of active, pending, or queued requests of this clients.\n"
  },
  {
    "path": "docs/docs/api/Connector.md",
    "content": "# Connector\n\nUndici creates the underlying socket via the connector builder.\nNormally, this happens automatically and you don't need to care about this,\nbut if you need to perform some additional check over the currently used socket,\nthis is the right place.\n\nIf you want to create a custom connector, you must import the `buildConnector` utility.\n\n#### Parameter: `buildConnector.BuildOptions`\n\nEvery Tls option, see [here](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).\nFurthermore, the following options can be passed:\n\n* **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.\n* **maxCachedSessions** `number | null` (optional) - Default: `100` - Maximum number of TLS cached sessions. Use 0 to disable TLS session caching. Default: `100`.\n* **timeout** `number | null` (optional) -  In milliseconds. Default `10e3`.\n* **servername** `string | null` (optional)\n\nOnce you call `buildConnector`, it will return a connector function, which takes the following parameters.\n\n#### Parameter: `connector.Options`\n\n* **hostname** `string` (required)\n* **host** `string` (optional)\n* **protocol** `string` (required)\n* **port** `string` (required)\n* **servername** `string` (optional)\n* **localAddress** `string | null` (optional) Local address the socket should connect from.\n* **httpSocket** `Socket` (optional) Establish secure connection on a given socket rather than creating a new socket. It can only be sent on TLS update.\n\n### Basic example\n\n```js\n'use strict'\n\nimport { Client, buildConnector } from 'undici'\n\nconst connector = buildConnector({ rejectUnauthorized: false })\nconst client = new Client('https://localhost:3000', {\n  connect (opts, cb) {\n    connector(opts, (err, socket) => {\n      if (err) {\n        cb(err)\n      } else if (/* assertion */) {\n        socket.destroy()\n        cb(new Error('kaboom'))\n      } else {\n        cb(null, socket)\n      }\n    })\n  }\n})\n```\n\n### Example: validate the CA fingerprint\n\n```js\n'use strict'\n\nimport { Client, buildConnector } from 'undici'\n\nconst caFingerprint = 'FO:OB:AR'\nconst connector = buildConnector({ rejectUnauthorized: false })\nconst client = new Client('https://localhost:3000', {\n  connect (opts, cb) {\n    connector(opts, (err, socket) => {\n      if (err) {\n        cb(err)\n      } else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {\n        socket.destroy()\n        cb(new Error('Fingerprint does not match or malformed certificate'))\n      } else {\n        cb(null, socket)\n      }\n    })\n  }\n})\n\nclient.request({\n  path: '/',\n  method: 'GET'\n}, (err, data) => {\n  if (err) throw err\n\n  const bufs = []\n  data.body.on('data', (buf) => {\n    bufs.push(buf)\n  })\n  data.body.on('end', () => {\n    console.log(Buffer.concat(bufs).toString('utf8'))\n    client.close()\n  })\n})\n\nfunction getIssuerCertificate (socket) {\n  let certificate = socket.getPeerCertificate(true)\n  while (certificate && Object.keys(certificate).length > 0) {\n    // invalid certificate\n    if (certificate.issuerCertificate == null) {\n      return null\n    }\n\n    // We have reached the root certificate.\n    // In case of self-signed certificates, `issuerCertificate` may be a circular reference.\n    if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {\n      break\n    }\n\n    // continue the loop\n    certificate = certificate.issuerCertificate\n  }\n  return certificate\n}\n```\n"
  },
  {
    "path": "docs/docs/api/ContentType.md",
    "content": "# MIME Type Parsing\n\n## `MIMEType` interface\n\n* **type** `string`\n* **subtype** `string`\n* **parameters** `Map<string, string>`\n* **essence** `string`\n\n## `parseMIMEType(input)`\n\nImplements [parse a MIME type](https://mimesniff.spec.whatwg.org/#parse-a-mime-type).\n\nParses a MIME type, returning its type, subtype, and any associated parameters. If the parser can't parse an input it returns the string literal `'failure'`.\n\n```js\nimport { parseMIMEType } from 'undici'\n\nparseMIMEType('text/html; charset=gbk')\n// {\n//   type: 'text',\n//   subtype: 'html',\n//   parameters: Map(1) { 'charset' => 'gbk' },\n//   essence: 'text/html'\n// }\n```\n\nArguments:\n\n* **input** `string`\n\nReturns: `MIMEType|'failure'`\n\n## `serializeAMimeType(input)`\n\nImplements [serialize a MIME type](https://mimesniff.spec.whatwg.org/#serialize-a-mime-type).\n\nSerializes a MIMEType object.\n\n```js\nimport { serializeAMimeType } from 'undici'\n\nserializeAMimeType({\n  type: 'text',\n  subtype: 'html',\n  parameters: new Map([['charset', 'gbk']]),\n  essence: 'text/html'\n})\n// text/html;charset=gbk\n\n```\n\nArguments:\n\n* **mimeType** `MIMEType`\n\nReturns: `string`\n"
  },
  {
    "path": "docs/docs/api/Cookies.md",
    "content": "# Cookie Handling\n\n## `Cookie` interface\n\n* **name** `string`\n* **value** `string`\n* **expires** `Date|number` (optional)\n* **maxAge** `number` (optional)\n* **domain** `string` (optional)\n* **path** `string` (optional)\n* **secure** `boolean` (optional)\n* **httpOnly** `boolean` (optional)\n* **sameSite** `'String'|'Lax'|'None'` (optional)\n* **unparsed** `string[]` (optional) Left over attributes that weren't parsed.\n\n## `deleteCookie(headers, name[, attributes])`\n\nSets the expiry time of the cookie to the unix epoch, causing browsers to delete it when received.\n\n```js\nimport { deleteCookie, Headers } from 'undici'\n\nconst headers = new Headers()\ndeleteCookie(headers, 'name')\n\nconsole.log(headers.get('set-cookie')) // name=; Expires=Thu, 01 Jan 1970 00:00:00 GMT\n```\n\nArguments:\n\n* **headers** `Headers`\n* **name** `string`\n* **attributes** `{ path?: string, domain?: string }` (optional)\n\nReturns: `void`\n\n## `getCookies(headers)`\n\nParses the `Cookie` header and returns a list of attributes and values.\n\n```js\nimport { getCookies, Headers } from 'undici'\n\nconst headers = new Headers({\n  cookie: 'get=cookies; and=attributes'\n})\n\nconsole.log(getCookies(headers)) // { get: 'cookies', and: 'attributes' }\n```\n\nArguments:\n\n* **headers** `Headers`\n\nReturns: `Record<string, string>`\n\n## `getSetCookies(headers)`\n\nParses all `Set-Cookie` headers.\n\n```js\nimport { getSetCookies, Headers } from 'undici'\n\nconst headers = new Headers({ 'set-cookie': 'undici=getSetCookies; Secure' })\n\nconsole.log(getSetCookies(headers))\n// [\n//   {\n//     name: 'undici',\n//     value: 'getSetCookies',\n//     secure: true\n//   }\n// ]\n\n```\n\nArguments:\n\n* **headers** `Headers`\n\nReturns: `Cookie[]`\n\n## `setCookie(headers, cookie)`\n\nAppends a cookie to the `Set-Cookie` header.\n\n```js\nimport { setCookie, Headers } from 'undici'\n\nconst headers = new Headers()\nsetCookie(headers, { name: 'undici', value: 'setCookie' })\n\nconsole.log(headers.get('Set-Cookie')) // undici=setCookie\n```\n\nArguments:\n\n* **headers** `Headers`\n* **cookie** `Cookie`\n\nReturns: `void`\n"
  },
  {
    "path": "docs/docs/api/Debug.md",
    "content": "# Debug\n\nUndici (and subsenquently `fetch` and `websocket`) exposes a debug statement that can be enabled by setting `NODE_DEBUG` within the environment.\n\nThe flags available are:\n\n## `undici`\n\nThis flag enables debug statements for the core undici library.\n\n```sh\nNODE_DEBUG=undici node script.js\n\nUNDICI 16241: connecting to nodejs.org using https:h1\nUNDICI 16241: connecting to nodejs.org using https:h1\nUNDICI 16241: connected to nodejs.org using https:h1\nUNDICI 16241: sending request to GET https://nodejs.org/\nUNDICI 16241: received response to GET https://nodejs.org/ - HTTP 307\nUNDICI 16241: connecting to nodejs.org using https:h1\nUNDICI 16241: trailers received from GET https://nodejs.org/\nUNDICI 16241: connected to nodejs.org using https:h1\nUNDICI 16241: sending request to GET https://nodejs.org/en\nUNDICI 16241: received response to GET https://nodejs.org/en - HTTP 200\nUNDICI 16241: trailers received from GET https://nodejs.org/en\n```\n\n## `fetch`\n\nThis flag enables debug statements for the `fetch` API.\n\n> **Note**: statements are pretty similar to the ones in the `undici` flag, but scoped to `fetch`\n\n```sh\nNODE_DEBUG=fetch node script.js\n\nFETCH 16241: connecting to nodejs.org using https:h1\nFETCH 16241: connecting to nodejs.org using https:h1\nFETCH 16241: connected to nodejs.org using https:h1\nFETCH 16241: sending request to GET https://nodejs.org/\nFETCH 16241: received response to GET https://nodejs.org/ - HTTP 307\nFETCH 16241: connecting to nodejs.org using https:h1\nFETCH 16241: trailers received from GET https://nodejs.org/\nFETCH 16241: connected to nodejs.org using https:h1\nFETCH 16241: sending request to GET https://nodejs.org/en\nFETCH 16241: received response to GET https://nodejs.org/en - HTTP 200\nFETCH 16241: trailers received from GET https://nodejs.org/en\n```\n\n## `websocket`\n\nThis flag enables debug statements for the `Websocket` API.\n\n> **Note**: statements can overlap with `UNDICI` ones if `undici` or `fetch` flag has been enabled as well.\n\n```sh\nNODE_DEBUG=websocket node script.js\n\nWEBSOCKET 18309: connecting to echo.websocket.org using https:h1\nWEBSOCKET 18309: connected to echo.websocket.org using https:h1\nWEBSOCKET 18309: sending request to GET https://echo.websocket.org/\nWEBSOCKET 18309: connection opened <ip_address>\n```\n"
  },
  {
    "path": "docs/docs/api/DiagnosticsChannel.md",
    "content": "# Diagnostics Channel Support\n\nStability: Experimental.\n\nUndici supports the [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) (currently available only on Node.js v16+).\nIt is the preferred way to instrument Undici and retrieve internal information.\n\nThe channels available are the following.\n\n## `undici:request:create`\n\nThis message is published when a new outgoing request is created.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n  console.log('origin', request.origin)\n  console.log('completed', request.completed)\n  console.log('method', request.method)\n  console.log('path', request.path)\n  console.log('headers', request.headers) // array of strings, e.g: ['foo', 'bar']\n  request.addHeader('hello', 'world')\n  console.log('headers', request.headers) // e.g. ['foo', 'bar', 'hello', 'world']\n})\n```\n\nNote: a request is only loosely completed to a given socket.\n\n## `undici:request:bodyChunkSent`\n\nThis message is published when a chunk of the request body is being sent.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:bodyChunkSent').subscribe(({ request, chunk }) => {\n  // request is the same object undici:request:create\n})\n```\n\n## `undici:request:bodySent`\n\nThis message is published after the request body has been fully sent.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {\n  // request is the same object undici:request:create\n})\n```\n\n## `undici:request:headers`\n\nThis message is published after the response headers have been received.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => {\n  // request is the same object undici:request:create\n  console.log('statusCode', response.statusCode)\n  console.log(response.statusText)\n  // response.headers are buffers.\n  console.log(response.headers.map((x) => x.toString()))\n})\n```\n\n## `undici:request:bodyChunkReceived`\n\nThis message is published after a chunk of the response body has been received.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ request, chunk }) => {\n  // request is the same object undici:request:create\n})\n```\n\n## `undici:request:trailers`\n\nThis message is published after the response body and trailers have been received, i.e. the response has been completed.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:trailers').subscribe(({ request, trailers }) => {\n  // request is the same object undici:request:create\n  console.log('completed', request.completed)\n  // trailers are buffers.\n  console.log(trailers.map((x) => x.toString()))\n})\n```\n\n## `undici:request:error`\n\nThis message is published if the request is going to error, but it has not errored yet.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:error').subscribe(({ request, error }) => {\n  // request is the same object undici:request:create\n})\n```\n\n## `undici:client:sendHeaders`\n\nThis message is published right before the first byte of the request is written to the socket.\n\n*Note*: It will publish the exact headers that will be sent to the server in raw format.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ request, headers, socket }) => {\n  // request is the same object undici:request:create\n  console.log(`Full headers list ${headers.split('\\r\\n')}`);\n})\n```\n\n## `undici:client:beforeConnect`\n\nThis message is published before creating a new connection for **any** request.\nYou can not assume that this event is related to any specific request.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {\n  // const { host, hostname, protocol, port, servername, version } = connectParams\n  // connector is a function that creates the socket\n})\n```\n\n## `undici:client:connected`\n\nThis message is published after a connection is established.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:client:connected').subscribe(({ socket, connectParams, connector }) => {\n  // const { host, hostname, protocol, port, servername, version } = connectParams\n // connector is a function that creates the socket\n})\n```\n\n## `undici:client:connectError`\n\nThis message is published if it did not succeed to create new connection\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, socket, connectParams, connector }) => {\n  // const { host, hostname, protocol, port, servername, version } = connectParams\n  // connector is a function that creates the socket\n  console.log(`Connect failed with ${error.message}`)\n})\n```\n\n## `undici:websocket:open`\n\nThis message is published after the client has successfully connected to a server.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:websocket:open').subscribe(({ \n  address,           // { address: string, family: string, port: number }\n  protocol,          // string - negotiated subprotocol\n  extensions,        // string - negotiated extensions\n  websocket,         // WebSocket - the WebSocket instance\n  handshakeResponse  // object - HTTP response that upgraded the connection\n}) => {\n  console.log(address) // address, family, and port\n  console.log(protocol) // negotiated subprotocols\n  console.log(extensions) // negotiated extensions\n  console.log(websocket) // the WebSocket instance\n  \n  // Handshake response details\n  console.log(handshakeResponse.status) // 101 for successful WebSocket upgrade\n  console.log(handshakeResponse.statusText) // 'Switching Protocols'\n  console.log(handshakeResponse.headers) // Object containing response headers\n})\n```\n\n### Handshake Response Object\n\nThe `handshakeResponse` object contains the HTTP response that upgraded the connection to WebSocket:\n\n- `status` (number): The HTTP status code (101 for successful WebSocket upgrade)\n- `statusText` (string): The HTTP status message ('Switching Protocols' for successful upgrade)\n- `headers` (object): The HTTP response headers from the server, including:\n  - `upgrade: 'websocket'`\n  - `connection: 'upgrade'`\n  - `sec-websocket-accept` and other WebSocket-related headers\n\nThis information is particularly useful for debugging and monitoring WebSocket connections, as it provides access to the initial HTTP handshake response that established the WebSocket connection.\n\n## `undici:websocket:close`\n\nThis message is published after the connection has closed.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:websocket:close').subscribe(({ websocket, code, reason }) => {\n  console.log(websocket) // the WebSocket instance\n  console.log(code) // the closing status code\n  console.log(reason) // the closing reason\n})\n```\n\n## `undici:websocket:socket_error`\n\nThis message is published if the socket experiences an error.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:websocket:socket_error').subscribe((error) => {\n  console.log(error)\n})\n```\n\n## `undici:websocket:ping`\n\nThis message is published after the client receives a ping frame, if the connection is not closing.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload, websocket }) => {\n  // a Buffer or undefined, containing the optional application data of the frame\n  console.log(payload)\n  console.log(websocket) // the WebSocket instance\n})\n```\n\n## `undici:websocket:pong`\n\nThis message is published after the client receives a pong frame.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload, websocket }) => {\n  // a Buffer or undefined, containing the optional application data of the frame\n  console.log(payload)\n  console.log(websocket) // the WebSocket instance\n})\n```\n\n## `undici:proxy:connected`\n\nThis message is published after the `ProxyAgent` establishes a connection to the proxy server.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:proxy:connected').subscribe(({ socket, connectParams }) => {\n  console.log(socket)\n  console.log(connectParams)\n  // const { origin, port, path, signal, headers, servername } = connectParams\n})\n```\n\n## `undici:request:pending-requests`\n\nThis message is published when the deduplicate interceptor's pending request map changes. This is useful for monitoring and debugging request deduplication behavior.\n\nThe deduplicate interceptor automatically deduplicates concurrent requests for the same resource. When multiple identical requests are made while one is already in-flight, only one request is sent to the origin server, and all waiting handlers receive the same response.\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\ndiagnosticsChannel.channel('undici:request:pending-requests').subscribe(({ type, size, key }) => {\n  console.log(type)  // 'added' or 'removed'\n  console.log(size)  // current number of pending requests\n  console.log(key)   // the deduplication key for this request\n})\n```\n\n### Event Properties\n\n- `type` (`string`): Either `'added'` when a new pending request is registered, or `'removed'` when a pending request completes (successfully or with an error).\n- `size` (`number`): The current number of pending requests after the change.\n- `key` (`string`): The deduplication key for the request, composed of the origin, method, path, and request headers.\n\n### Example: Monitoring Request Deduplication\n\n```js\nimport diagnosticsChannel from 'diagnostics_channel'\n\nconst channel = diagnosticsChannel.channel('undici:request:pending-requests')\n\nchannel.subscribe(({ type, size, key }) => {\n  if (type === 'added') {\n    console.log(`New pending request: ${key} (${size} total pending)`)\n  } else {\n    console.log(`Request completed: ${key} (${size} remaining)`)\n  }\n})\n```\n\nThis can be useful for:\n- Verifying that request deduplication is working as expected\n- Monitoring the number of concurrent in-flight requests\n- Debugging deduplication behavior in production environments\n"
  },
  {
    "path": "docs/docs/api/Dispatcher.md",
    "content": "# Dispatcher\n\nExtends: `events.EventEmitter`\n\nDispatcher is the core API used to dispatch requests.\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## Instance Methods\n\n### `Dispatcher.close([callback]): Promise`\n\nCloses the dispatcher and gracefully waits for enqueued requests to complete before resolving.\n\nArguments:\n\n* **callback** `(error: Error | null, data: null) => void` (optional)\n\nReturns: `void | Promise<null>` - Only returns a `Promise` if no `callback` argument was passed\n\n```js\ndispatcher.close() // -> Promise\ndispatcher.close(() => {}) // -> void\n```\n\n#### Example - Request resolves before Client closes\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('undici')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n  })\n  body.setEncoding('utf8')\n  body.on('data', console.log)\n} catch (error) {}\n\nawait client.close()\n\nconsole.log('Client closed')\nserver.close()\n```\n\n### `Dispatcher.connect(options[, callback])`\n\nStarts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).\n\nArguments:\n\n* **options** `ConnectOptions`\n* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)\n\nReturns: `void | Promise<ConnectData>` - Only returns a `Promise` if no `callback` argument was passed\n\n#### Parameter: `ConnectOptions`\n\n* **path** `string`\n* **headers** `UndiciHeaders` (optional) - Default: `null`\n* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`\n* **opaque** `unknown` (optional) - This argument parameter is passed through to `ConnectData`\n\n#### Parameter: `ConnectData`\n\n* **statusCode** `number`\n* **headers** `Record<string, string | string[] | undefined>`\n* **socket** `stream.Duplex`\n* **opaque** `unknown`\n\n#### Example - Connect request with echo\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  throw Error('should never get here')\n}).listen()\n\nserver.on('connect', (req, socket, head) => {\n  socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n  let data = head.toString()\n  socket.on('data', (buf) => {\n    data += buf.toString()\n  })\n\n  socket.on('end', () => {\n    socket.end(data)\n  })\n})\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const { socket } = await client.connect({\n    path: '/'\n  })\n  const wanted = 'Body'\n  let data = ''\n  socket.on('data', d => { data += d })\n  socket.on('end', () => {\n    console.log(`Data received: ${data.toString()} | Data wanted: ${wanted}`)\n    client.close()\n    server.close()\n  })\n  socket.write(wanted)\n  socket.end()\n} catch (error) { }\n```\n\n### `Dispatcher.destroy([error, callback]): Promise`\n\nDestroy the dispatcher abruptly with the given error. All the pending and running requests will be asynchronously aborted and error. Since this operation is asynchronously dispatched there might still be some progress on dispatched requests.\n\nBoth arguments are optional; the method can be called in four different ways:\n\nArguments:\n\n* **error** `Error | null` (optional)\n* **callback** `(error: Error | null, data: null) => void` (optional)\n\nReturns: `void | Promise<void>` - Only returns a `Promise` if no `callback` argument was passed\n\n```js\ndispatcher.destroy() // -> Promise\ndispatcher.destroy(new Error()) // -> Promise\ndispatcher.destroy(() => {}) // -> void\ndispatcher.destroy(new Error(), () => {}) // -> void\n```\n\n#### Example - Request is aborted when Client is destroyed\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end()\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const request = client.request({\n    path: '/',\n    method: 'GET'\n  })\n  client.destroy()\n    .then(() => {\n      console.log('Client destroyed')\n      server.close()\n    })\n  await request\n} catch (error) {\n  console.error(error)\n}\n```\n\n### `Dispatcher.dispatch(options, handler)`\n\nThis is the low level API which all the preceding APIs are implemented on top of.\nThis API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs.\nIt is primarily intended for library developers who implement higher level APIs on top of this.\n\nArguments:\n\n* **options** `DispatchOptions`\n* **handler** `DispatchHandler`\n\nReturns: `Boolean` - `false` if dispatcher is busy and further dispatch calls won't make any progress until the `'drain'` event has been emitted.\n\n#### Parameter: `DispatchOptions`\n\n* **origin** `string | URL`\n* **path** `string`\n* **method** `string`\n* **reset** `boolean` (optional) - Default: `false` - If `false`, the request will attempt to create a long-living connection by sending the `connection: keep-alive` header,otherwise will attempt to close it immediately after response by sending `connection: close` within the request and closing the socket afterwards.\n* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`\n* **headers** `UndiciHeaders` (optional) - Default: `null`.\n* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.\n* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.\n* **blocking** `boolean` (optional) - Default: `method !== 'HEAD'` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.\n* **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.\n* **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds.\n* **headersTimeout** `number | null` (optional) - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.\n* **expectContinue** `boolean` (optional) - Default: `false` - For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server\n\n#### Parameter: `DispatchHandler`\n\n* **onRequestStart** `(controller: DispatchController, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.\n* **onRequestUpgrade** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string[]>, socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.\n* **onResponseStart** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string []>, statusMessage?: string) => void` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests. Any return value is ignored.\n* **onResponseData** `(controller: DispatchController, chunk: Buffer) => void` - Invoked when response payload data is received. Not required for `upgrade` requests.\n* **onResponseEnd** `(controller: DispatchController, trailers: Record<string, string | string[]>) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.\n* **onResponseError** `(controller: DispatchController, error: Error) => void` - Invoked when an error has occurred. May not throw.\n\n#### Example 1 - Dispatch GET request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nconst data = []\n\nclient.dispatch({\n  path: '/',\n  method: 'GET',\n  headers: {\n    'x-foo': 'bar'\n  }\n}, {\n  onConnect: () => {\n    console.log('Connected!')\n  },\n  onError: (error) => {\n    console.error(error)\n  },\n  onHeaders: (statusCode, headers) => {\n    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)\n  },\n  onData: (chunk) => {\n    console.log('onData: chunk received')\n    data.push(chunk)\n  },\n  onComplete: (trailers) => {\n    console.log(`onComplete | trailers: ${trailers}`)\n    const res = Buffer.concat(data).toString('utf8')\n    console.log(`Data: ${res}`)\n    client.close()\n    server.close()\n  }\n})\n```\n\n#### Example 2 - Dispatch Upgrade Request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end()\n}).listen()\n\nawait once(server, 'listening')\n\nserver.on('upgrade', (request, socket, head) => {\n  console.log('Node.js Server - upgrade event')\n  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\\r\\n')\n  socket.write('Upgrade: WebSocket\\r\\n')\n  socket.write('Connection: Upgrade\\r\\n')\n  socket.write('\\r\\n')\n  socket.end()\n})\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nclient.dispatch({\n  path: '/',\n  method: 'GET',\n  upgrade: 'websocket'\n}, {\n  onConnect: () => {\n    console.log('Undici Client - onConnect')\n  },\n  onError: (error) => {\n    console.log('onError') // shouldn't print\n  },\n  onUpgrade: (statusCode, headers, socket) => {\n    console.log('Undici Client - onUpgrade')\n    console.log(`onUpgrade Headers: ${headers}`)\n    socket.on('data', buffer => {\n      console.log(buffer.toString('utf8'))\n    })\n    socket.on('end', () => {\n      client.close()\n      server.close()\n    })\n    socket.end()\n  }\n})\n```\n\n#### Example 3 - Dispatch POST request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  request.on('data', (data) => {\n    console.log(`Request Data: ${data.toString('utf8')}`)\n    const body = JSON.parse(data)\n    body.message = 'World'\n    response.end(JSON.stringify(body))\n  })\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nconst data = []\n\nclient.dispatch({\n  path: '/',\n  method: 'POST',\n  headers: {\n    'content-type': 'application/json'\n  },\n  body: JSON.stringify({ message: 'Hello' })\n}, {\n  onConnect: () => {\n    console.log('Connected!')\n  },\n  onError: (error) => {\n    console.error(error)\n  },\n  onHeaders: (statusCode, headers) => {\n    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)\n  },\n  onData: (chunk) => {\n    console.log('onData: chunk received')\n    data.push(chunk)\n  },\n  onComplete: (trailers) => {\n    console.log(`onComplete | trailers: ${trailers}`)\n    const res = Buffer.concat(data).toString('utf8')\n    console.log(`Response Data: ${res}`)\n    client.close()\n    server.close()\n  }\n})\n```\n\n### `Dispatcher.pipeline(options, handler)`\n\nFor easy use with [stream.pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback). The `handler` argument should return a `Readable` from which the result will be read. Usually it should just return the `body` argument unless some kind of transformation needs to be performed based on e.g. `headers` or `statusCode`. The `handler` should validate the response and save any required state. If there is an error, it should be thrown. The function returns a `Duplex` which writes to the request and reads from the response.\n\nArguments:\n\n* **options** `PipelineOptions`\n* **handler** `(data: PipelineHandlerData) => stream.Readable`\n\nReturns: `stream.Duplex`\n\n#### Parameter: PipelineOptions\n\nExtends: [`RequestOptions`](/docs/docs/api/Dispatcher.md#parameter-requestoptions)\n\n* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.\n\n#### Parameter: PipelineHandlerData\n\n* **statusCode** `number`\n* **headers** `Record<string, string | string[] | undefined>`\n* **opaque** `unknown`\n* **body** `stream.Readable`\n* **context** `object`\n* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.\n\n#### Example 1 - Pipeline Echo\n\n```js\nimport { Readable, Writable, PassThrough, pipeline } from 'stream'\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  request.pipe(response)\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nlet res = ''\n\npipeline(\n  new Readable({\n    read () {\n      this.push(Buffer.from('undici'))\n      this.push(null)\n    }\n  }),\n  client.pipeline({\n    path: '/',\n    method: 'GET'\n  }, ({ statusCode, headers, body }) => {\n    console.log(`response received ${statusCode}`)\n    console.log('headers', headers)\n    return pipeline(body, new PassThrough(), () => {})\n  }),\n  new Writable({\n    write (chunk, _, callback) {\n      res += chunk.toString()\n      callback()\n    },\n    final (callback) {\n      console.log(`Response pipelined to writable: ${res}`)\n      callback()\n    }\n  }),\n  error => {\n    if (error) {\n      console.error(error)\n    }\n\n    client.close()\n    server.close()\n  }\n)\n```\n\n### `Dispatcher.request(options[, callback])`\n\nPerforms a HTTP request.\n\nNon-idempotent requests will not be pipelined in order\nto avoid indirect failures.\n\nIdempotent requests will be automatically retried if\nthey fail due to indirect failure from the request\nat the head of the pipeline. This does not apply to\nidempotent requests with a stream request body.\n\nAll response bodies must always be fully consumed or destroyed.\n\nArguments:\n\n* **options** `RequestOptions`\n* **callback** `(error: Error | null, data: ResponseData) => void` (optional)\n\nReturns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.\n\n#### Parameter: `RequestOptions`\n\nExtends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions)\n\n* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.\n* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.\n* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.\n\nThe `RequestOptions.method` property should not be value `'CONNECT'`.\n\n#### Parameter: `ResponseData`\n\n* **statusCode** `number`\n* **statusText** `string` - The status message from the response (e.g., \"OK\", \"Not Found\").\n* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e.g. `content-type`.\n* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).\n* **trailers** `Record<string, string>` - This object starts out\n  as empty and will be mutated to contain trailers after `body` has emitted `'end'`.\n* **opaque** `unknown`\n* **context** `object`\n\n`body` contains the following additional [body mixin](https://fetch.spec.whatwg.org/#body-mixin) methods and properties:\n\n* [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)\n* [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)\n* [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)\n* [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)\n* [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)\n* `body`\n* `bodyUsed`\n\n`body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.\n\n`body` contains the following additional extensions:\n\n- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.\n\nNote that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.\n\n#### Example 1 - Basic GET Request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const { body, headers, statusCode, statusText, trailers } = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n  console.log(`response received ${statusCode}`)\n  console.log('headers', headers)\n  body.setEncoding('utf8')\n  body.on('data', console.log)\n  body.on('error', console.error)\n  body.on('end', () => {\n    console.log('trailers', trailers)\n  })\n\n  client.close()\n  server.close()\n} catch (error) {\n  console.error(error)\n}\n```\n\n#### Example 2 - Aborting a request\n\n> Node.js v15+ is required to run this example\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\nconst abortController = new AbortController()\n\ntry {\n  client.request({\n    path: '/',\n    method: 'GET',\n    signal: abortController.signal\n  })\n} catch (error) {\n  console.error(error) // should print an RequestAbortedError\n  client.close()\n  server.close()\n}\n\nabortController.abort()\n```\n\nAlternatively, any `EventEmitter` that emits an `'abort'` event may be used as an abort controller:\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport EventEmitter, { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\nconst ee = new EventEmitter()\n\ntry {\n  client.request({\n    path: '/',\n    method: 'GET',\n    signal: ee\n  })\n} catch (error) {\n  console.error(error) // should print an RequestAbortedError\n  client.close()\n  server.close()\n}\n\nee.emit('abort')\n```\n\nDestroying the request or response body will have the same effect.\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const { body } = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n  body.destroy()\n} catch (error) {\n  console.error(error) // should print an RequestAbortedError\n  client.close()\n  server.close()\n}\n```\n\n#### Example 3 - Conditionally reading the body\n\nRemember to fully consume the body even in the case when it is not read.\n\n```js\nconst { body, statusCode } = await client.request({\n  path: '/',\n  method: 'GET'\n})\n\nif (statusCode === 200) {\n  return await body.arrayBuffer()\n}\n\nawait body.dump()\n\nreturn null\n```\n\n### `Dispatcher.stream(options, factory[, callback])`\n\nA faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.\n\nAs demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatch.md#example-2-stream-to-fastify-response) for more details.\n\nArguments:\n\n* **options** `RequestOptions`\n* **factory** `(data: StreamFactoryData) => stream.Writable`\n* **callback** `(error: Error | null, data: StreamData) => void` (optional)\n\nReturns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback` argument was passed\n\n#### Parameter: `StreamFactoryData`\n\n* **statusCode** `number`\n* **headers** `Record<string, string | string[] | undefined>`\n* **opaque** `unknown`\n* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.\n\n#### Parameter: `StreamData`\n\n* **opaque** `unknown`\n* **trailers** `Record<string, string>`\n* **context** `object`\n\n#### Example 1 - Basic GET stream request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\nimport { Writable } from 'stream'\n\nconst server = createServer((request, response) => {\n  response.end('Hello, World!')\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\nconst bufs = []\n\ntry {\n  await client.stream({\n    path: '/',\n    method: 'GET',\n    opaque: { bufs }\n  }, ({ statusCode, headers, opaque: { bufs } }) => {\n    console.log(`response received ${statusCode}`)\n    console.log('headers', headers)\n    return new Writable({\n      write (chunk, encoding, callback) {\n        bufs.push(chunk)\n        callback()\n      }\n    })\n  })\n\n  console.log(Buffer.concat(bufs).toString('utf-8'))\n\n  client.close()\n  server.close()\n} catch (error) {\n  console.error(error)\n}\n```\n\n#### Example 2 - Stream to Fastify Response\n\nIn this example, a (fake) request is made to the fastify server using `fastify.inject()`. This request then executes the fastify route handler which makes a subsequent request to the raw Node.js http server using `undici.dispatcher.stream()`. The fastify response is passed to the `opaque` option so that undici can tap into the underlying writable stream using `response.raw`. This methodology demonstrates how one could use undici and fastify together to create fast-as-possible requests from one backend server to another.\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\nimport fastify from 'fastify'\n\nconst nodeServer = createServer((request, response) => {\n  response.end('Hello, World! From Node.js HTTP Server')\n}).listen()\n\nawait once(nodeServer, 'listening')\n\nconsole.log('Node Server listening')\n\nconst nodeServerUndiciClient = new Client(`http://localhost:${nodeServer.address().port}`)\n\nconst fastifyServer = fastify()\n\nfastifyServer.route({\n  url: '/',\n  method: 'GET',\n  handler: (request, response) => {\n    nodeServerUndiciClient.stream({\n      path: '/',\n      method: 'GET',\n      opaque: response\n    }, ({ opaque }) => opaque.raw)\n  }\n})\n\nawait fastifyServer.listen()\n\nconsole.log('Fastify Server listening')\n\nconst fastifyServerUndiciClient = new Client(`http://localhost:${fastifyServer.server.address().port}`)\n\ntry {\n  const { statusCode, body } = await fastifyServerUndiciClient.request({\n    path: '/',\n    method: 'GET'\n  })\n\n  console.log(`response received ${statusCode}`)\n  body.setEncoding('utf8')\n  body.on('data', console.log)\n\n  nodeServerUndiciClient.close()\n  fastifyServerUndiciClient.close()\n  fastifyServer.close()\n  nodeServer.close()\n} catch (error) { }\n```\n\n### `Dispatcher.upgrade(options[, callback])`\n\nUpgrade to a different protocol. Visit [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.\n\nArguments:\n\n* **options** `UpgradeOptions`\n\n* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)\n\nReturns: `void | Promise<UpgradeData>` - Only returns a `Promise` if no `callback` argument was passed\n\n#### Parameter: `UpgradeOptions`\n\n* **path** `string`\n* **method** `string` (optional) - Default: `'GET'`\n* **headers** `UndiciHeaders` (optional) - Default: `null`\n* **protocol** `string` (optional) - Default: `'Websocket'` - A string of comma separated protocols, in descending preference order.\n* **signal** `AbortSignal | EventEmitter | null` (optional) - Default: `null`\n\n#### Parameter: `UpgradeData`\n\n* **headers** `http.IncomingHeaders`\n* **socket** `stream.Duplex`\n* **opaque** `unknown`\n\n#### Example 1 - Basic Upgrade Request\n\n```js\nimport { createServer } from 'http'\nimport { Client } from 'undici'\nimport { once } from 'events'\n\nconst server = createServer((request, response) => {\n  response.statusCode = 101\n  response.setHeader('connection', 'upgrade')\n  response.setHeader('upgrade', request.headers.upgrade)\n  response.end()\n}).listen()\n\nawait once(server, 'listening')\n\nconst client = new Client(`http://localhost:${server.address().port}`)\n\ntry {\n  const { headers, socket } = await client.upgrade({\n    path: '/',\n  })\n  socket.on('end', () => {\n    console.log(`upgrade: ${headers.upgrade}`) // upgrade: Websocket\n    client.close()\n    server.close()\n  })\n  socket.end()\n} catch (error) {\n  console.error(error)\n  client.close()\n  server.close()\n}\n```\n\n### `Dispatcher.compose(interceptors[, interceptor])`\n\nCompose a new dispatcher from the current dispatcher and the given interceptors.\n\n> _Notes_:\n> - The order of the interceptors matters. The last interceptor will be the first to be called.\n> - It is important to note that the `interceptor` function should return a function that follows the `Dispatcher.dispatch` signature.\n> - Any fork of the chain of `interceptors` can lead to unexpected results.\n>\n> **Interceptor Stack Visualization:**\n> ```\n> compose([interceptor1, interceptor2, interceptor3])\n>\n> Request Flow:\n> ┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐\n> │   Request   │───▶│interceptor3 │───▶│interceptor2 │───▶│interceptor1 │───▶│  dispatcher │\n> └─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘    │   .dispatch │\n>                           ▲                   ▲                   ▲         └─────────────┘\n>                           │                   │                   │                ▲\n>                    (called first)      (called second)     (called last)           │\n>                                                                                    │\n> ┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐          │\n> │  Response   │◀───│interceptor3 │◀───│interceptor2 │◀───│interceptor1 │◀─────────┘\n> └─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘\n>\n> The interceptors are composed in reverse order due to function composition.\n> ```\n\nArguments:\n\n* **interceptors** `Interceptor[interceptor[]]`: It is an array of `Interceptor` functions passed as only argument, or several interceptors passed as separate arguments.\n\nReturns: `Dispatcher`.\n\n#### Parameter: `Interceptor`\n\nA function that takes a `dispatch` method and returns a `dispatch`-like function.\n\n#### Example 1 - Basic Compose\n\n```js\nconst { Client, RedirectHandler } = require('undici')\n\nconst redirectInterceptor = dispatch => {\n    return (opts, handler) => {\n      const { maxRedirections } = opts\n\n      if (!maxRedirections) {\n        return dispatch(opts, handler)\n      }\n\n      const redirectHandler = new RedirectHandler(\n        dispatch,\n        maxRedirections,\n        opts,\n        handler\n      )\n      opts = { ...opts, maxRedirections: 0 } // Stop sub dispatcher from also redirecting.\n      return dispatch(opts, redirectHandler)\n    }\n}\n\nconst client = new Client('http://localhost:3000')\n  .compose(redirectInterceptor)\n\nawait client.request({ path: '/', method: 'GET' })\n```\n\n#### Example 2 - Chained Compose\n\n```js\nconst { Client, RedirectHandler, RetryHandler } = require('undici')\n\nconst redirectInterceptor = dispatch => {\n    return (opts, handler) => {\n      const { maxRedirections } = opts\n\n      if (!maxRedirections) {\n        return dispatch(opts, handler)\n      }\n\n      const redirectHandler = new RedirectHandler(\n        dispatch,\n        maxRedirections,\n        opts,\n        handler\n      )\n      opts = { ...opts, maxRedirections: 0 }\n      return dispatch(opts, redirectHandler)\n    }\n}\n\nconst retryInterceptor = dispatch => {\n  return function retryInterceptor (opts, handler) {\n    return dispatch(\n      opts,\n      new RetryHandler(opts, {\n        handler,\n        dispatch\n      })\n    )\n  }\n}\n\nconst client = new Client('http://localhost:3000')\n  .compose(redirectInterceptor)\n  .compose(retryInterceptor)\n\nawait client.request({ path: '/', method: 'GET' })\n```\n\n#### Pre-built interceptors\n\n##### `redirect`\n\nThe `redirect` interceptor allows you to customize the way your dispatcher handles redirects.\n\nIt accepts the same arguments as the [`RedirectHandler` constructor](/docs/docs/api/RedirectHandler.md).\n\n**Example - Basic Redirect Interceptor**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { redirect } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  redirect({ maxRedirections: 3, throwOnMaxRedirects: true })\n);\nclient.request({ path: \"/\" })\n```\n\n##### `retry`\n\nThe `retry` interceptor allows you to customize the way your dispatcher handles retries.\n\nIt accepts the same arguments as the [`RetryHandler` constructor](/docs/docs/api/RetryHandler.md).\n\n**Example - Basic Redirect Interceptor**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { retry } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  retry({\n    maxRetries: 3,\n    minTimeout: 1000,\n    maxTimeout: 10000,\n    timeoutFactor: 2,\n    retryAfter: true,\n  })\n);\n```\n\n##### `dump`\n\nThe `dump` interceptor enables you to dump the response body from a request upon a given limit.\n\n**Options**\n- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the request's body exceeds this value then the connection will be closed. Default: `1048576`.\n\n> The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis.\n\n**Example - Basic Dump Interceptor**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { dump } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  dump({\n    maxSize: 1024,\n  })\n);\n\n// or\nclient.dispatch(\n  {\n    path: \"/\",\n    method: \"GET\",\n    dumpMaxSize: 1024,\n  },\n  handler\n);\n```\n\n##### `dns`\n\nThe `dns` interceptor enables you to cache DNS lookups for a given duration, per origin.\n\n>It is well suited for scenarios where you want to cache DNS lookups to avoid the overhead of resolving the same domain multiple times\n\n**Options**\n- `maxTTL` - The maximum time-to-live (in milliseconds) of the DNS cache. It should be a positive integer. Default: `10000`.\n  - Set `0` to disable TTL.\n- `maxItems` - The maximum number of items to cache. It should be a positive integer. Default: `Infinity`.\n- `dualStack` - Whether to resolve both IPv4 and IPv6 addresses. Default: `true`.\n  - It will also attempt a happy-eyeballs-like approach to connect to the available addresses in case of a connection failure.\n- `affinity` - Whether to use IPv4 or IPv6 addresses. Default: `4`.\n  - It can be either `'4` or `6`.\n  - It will only take effect if `dualStack` is `false`.\n- `lookup: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void` - Custom lookup function. Default: `dns.lookup`.\n  - For more info see [dns.lookup](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback).\n- `pick: (origin: URL, records: DNSInterceptorRecords, affinity: 4 | 6) => DNSInterceptorRecord` - Custom pick function. Default: `RoundRobin`.\n  - The function should return a single record from the records array.\n  - By default a simplified version of Round Robin is used.\n  - The `records` property can be mutated to store the state of the balancing algorithm.\n- `storage: DNSStorage` - Custom storage for resolved DNS records\n\n> The `Dispatcher#options` also gets extended with the options `dns.affinity`, `dns.dualStack`, `dns.lookup` and `dns.pick` which can be used to configure the interceptor at a request-per-request basis.\n\n\n**DNSInterceptorRecord**\nIt represents a DNS record.\n- `family` - (`number`) The IP family of the address. It can be either `4` or `6`.\n- `address` - (`string`) The IP address.\n\n**DNSInterceptorOriginRecords**\nIt represents a map of DNS IP addresses records for a single origin.\n- `4.ips` - (`DNSInterceptorRecord[] | null`) The IPv4 addresses.\n- `6.ips` - (`DNSInterceptorRecord[] | null`) The IPv6 addresses.\n\n**DNSStorage**\nIt represents a storage object for resolved DNS records.\n- `size` - (`number`) current size of the storage.\n- `get` - (`(origin: string) => DNSInterceptorOriginRecords | null`) method to get the records for a given origin.\n- `set` - (`(origin: string, records: DNSInterceptorOriginRecords | null, options: { ttl: number }) => void`) method to set the records for a given origin.\n- `delete` - (`(origin: string) => void`) method to delete records for a given origin.\n- `full` - (`() => boolean`) method to check if the storage is full, if returns `true`, DNS lookup will be skipped in this interceptor and new records will not be stored.\n\n**Example - Basic DNS Interceptor**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { dns } = interceptors;\n\nconst client = new Agent().compose([\n  dns({ ...opts })\n])\n\nconst response = await client.request({\n  origin: `http://localhost:3030`,\n  ...requestOpts\n})\n```\n\n**Example - DNS Interceptor and LRU cache as a storage**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst QuickLRU = require(\"quick-lru\");\nconst { dns } = interceptors;\n\nconst lru = new QuickLRU({ maxSize: 100 });\n\nconst lruAdapter = {\n  get size() {\n    return lru.size;\n  },\n  get(origin) {\n    return lru.get(origin);\n  },\n  set(origin, records, { ttl }) {\n    lru.set(origin, records, { maxAge: ttl });\n  },\n  delete(origin) {\n    lru.delete(origin);\n  },\n  full() {\n    // For LRU cache, we can always store new records,\n    // old records will be evicted automatically\n    return false;\n  }\n}\n\nconst client = new Agent().compose([\n  dns({ storage: lruAdapter })\n])\n\nconst response = await client.request({\n  origin: `http://localhost:3030`,\n  ...requestOpts\n})\n```\n\n##### `responseError`\n\nThe `responseError` interceptor throws an error for responses with status code errors (>= 400).\n\n**Example**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { responseError } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  responseError()\n);\n\n// Will throw a ResponseError for status codes >= 400\nawait client.request({\n  method: \"GET\",\n  path: \"/\"\n});\n```\n\n##### `decompress`\n\n⚠️ The decompress interceptor is experimental and subject to change.\n\nThe `decompress` interceptor automatically decompresses response bodies that are compressed with gzip, deflate, brotli, or zstd compression. It removes the `content-encoding` and `content-length` headers from decompressed responses and supports RFC-9110 compliant multiple encodings.\n\n**Options**\n\n- `skipErrorResponses` - Whether to skip decompression for error responses (status codes >= 400). Default: `true`.\n- `skipStatusCodes` - Array of status codes to skip decompression for. Default: `[204, 304]`.\n\n**Example - Basic Decompress Interceptor**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { decompress } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  decompress()\n);\n\n// Automatically decompresses gzip/deflate/brotli/zstd responses\nconst response = await client.request({\n  method: \"GET\",\n  path: \"/\"\n});\n```\n\n**Example - Custom Options**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { decompress } = interceptors;\n\nconst client = new Client(\"http://service.example\").compose(\n  decompress({\n    skipErrorResponses: false, // Decompress 5xx responses\n    skipStatusCodes: [204, 304, 201] // Skip these status codes\n  })\n);\n```\n\n**Supported Encodings**\n\n- `gzip` / `x-gzip` - GZIP compression\n- `deflate` / `x-compress` - DEFLATE compression  \n- `br` - Brotli compression\n- `zstd` - Zstandard compression\n- Multiple encodings (e.g., `gzip, deflate`) are supported per RFC-9110\n\n**Behavior**\n\n- Skips decompression for status codes < 200 or >= 400 (configurable)\n- Skips decompression for 204 No Content and 304 Not Modified by default\n- Removes `content-encoding` and `content-length` headers when decompressing\n- Passes through unsupported encodings unchanged\n- Handles case-insensitive encoding names\n- Supports streaming decompression without buffering\n\n##### `Cache Interceptor`\n\nThe `cache` interceptor implements client-side response caching as described in\n[RFC9111](https://www.rfc-editor.org/rfc/rfc9111.html).\n\n**Options**\n\n- `store` - The [`CacheStore`](/docs/docs/api/CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](/docs/docs/api/CacheStore.md#memorycachestore).\n- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.\n- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration and cannot have an heuristic expiry computed. If this isn't present, responses neither with an explicit expiration nor heuristically cacheable will not be cached. Default `undefined`.\n- `type` - The [type of cache](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching#types_of_caches) for Undici to act as. Can be `shared` or `private`. Default `shared`. `private` implies privately cacheable responses will be cached and potentially shared with other users of your application.\n\n**Usage with `fetch`**\n\n```js\nconst { Agent, cacheStores, interceptors, setGlobalDispatcher } = require('undici')\n\nconst client = new Agent().compose(interceptors.cache({\n  store: new cacheStores.MemoryCacheStore({\n    maxSize: 100 * 1024 * 1024, // 100MB\n    maxCount: 1000,\n    maxEntrySize: 5 * 1024 * 1024 // 5MB\n  })\n}))\n\nsetGlobalDispatcher(client)\n\n// First request goes to the network and is cached when cache headers allow it.\nconst first = await fetch('https://example.com/data')\n\n// Second request can be served from cache according to RFC9111 rules.\nconst second = await fetch('https://example.com/data')\n```\n\n##### `Deduplicate Interceptor`\n\nThe `deduplicate` interceptor deduplicates concurrent identical requests. When multiple identical requests are made while one is already in-flight, only one request is sent to the origin server, and all waiting handlers receive the same response. This reduces server load and improves performance.\n\n**Options**\n\n- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to deduplicate. Default `['GET']`.\n- `skipHeaderNames` - Header names that, if present in a request, will cause the request to skip deduplication entirely. Useful for headers like `idempotency-key` where presence indicates unique processing. Header name matching is case-insensitive. Default `[]`.\n- `excludeHeaderNames` - Header names to exclude from the deduplication key. Requests with different values for these headers will still be deduplicated together. Useful for headers like `x-request-id` that vary per request but shouldn't affect deduplication. Header name matching is case-insensitive. Default `[]`.\n- `maxBufferSize` - Maximum bytes buffered per paused waiting deduplicated handler. If a waiting handler remains paused and exceeds this threshold, it is failed with an abort error to prevent unbounded memory growth. Default `5 * 1024 * 1024`.\n\n**Usage**\n\n```js\nconst { Client, interceptors } = require(\"undici\");\nconst { deduplicate, cache } = interceptors;\n\n// Deduplicate only\nconst client = new Client(\"http://service.example\").compose(\n  deduplicate()\n);\n\n// Deduplicate with caching\nconst clientWithCache = new Client(\"http://service.example\").compose(\n  deduplicate(),\n  cache()\n);\n```\n\nRequests are considered identical if they have the same:\n- Origin\n- HTTP method\n- Path\n- Request headers (excluding any headers specified in `excludeHeaderNames`)\n\nAll deduplicated requests receive the complete response including status code, headers, and body.\n\nFor observability, request deduplication events are published to the `undici:request:pending-requests` [diagnostic channel](/docs/docs/api/DiagnosticsChannel.md#undicirequestpending-requests).\n\n## Instance Events\n\n### Event: `'connect'`\n\nParameters:\n\n* **origin** `URL`\n* **targets** `Array<Dispatcher>`\n\n### Event: `'disconnect'`\n\nParameters:\n\n* **origin** `URL`\n* **targets** `Array<Dispatcher>`\n* **error** `Error`\n\nEmitted when the dispatcher has been disconnected from the origin.\n\n> **Note**: For HTTP/2, this event is also emitted when the dispatcher has received the [GOAWAY Frame](https://webconcepts.info/concepts/http2-frame-type/0x7) with an Error with the message `HTTP/2: \"GOAWAY\" frame received` and the code `UND_ERR_INFO`.\n> Due to nature of the protocol of using binary frames, it is possible that requests gets hanging as a frame can be received between the `HEADER` and `DATA` frames.\n> It is recommended to handle this event and close the dispatcher to create a new HTTP/2 session.\n\n### Event: `'connectionError'`\n\nParameters:\n\n* **origin** `URL`\n* **targets** `Array<Dispatcher>`\n* **error** `Error`\n\nEmitted when dispatcher fails to connect to\norigin.\n\n### Event: `'drain'`\n\nParameters:\n\n* **origin** `URL`\n\nEmitted when dispatcher is no longer busy.\n\n## Parameter: `UndiciHeaders`\n\n* `Record<string, string | string[] | undefined> | string[] | Iterable<[string, string | string[] | undefined]> | null`\n\nHeader arguments such as `options.headers` in [`Client.dispatch`](/docs/docs/api/Client.md#clientdispatchoptions-handlers) can be specified in three forms:\n* As an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type.\n* As an array of strings. An array representation of a header list must have an even length, or an `InvalidArgumentError` will be thrown.\n* As an iterable that can encompass `Headers`, `Map`, or a custom iterator returning key-value pairs.\nKeys are lowercase and values are not modified.\n\nUndici validates header syntax at the protocol level (for example, invalid header names and invalid control characters in string values), but it does not sanitize untrusted application input. Validate and sanitize any user-provided header names and values before passing them to Undici to prevent header/body injection vulnerabilities.\n\nWhen using the array header format (`string[]`), Undici processes only indexed elements. Additional properties assigned to the array object are ignored.\n\nResponse headers will derive a `host` from the `url` of the [Client](/docs/docs/api/Client.md#class-client) instance if no `host` header was previously specified.\n\n### Example 1 - Object\n\n```js\n{\n  'content-length': '123',\n  'content-type': 'text/plain',\n  connection: 'keep-alive',\n  host: 'mysite.com',\n  accept: '*/*'\n}\n```\n\n### Example 2 - Array\n\n```js\n[\n  'content-length', '123',\n  'content-type', 'text/plain',\n  'connection', 'keep-alive',\n  'host', 'mysite.com',\n  'accept', '*/*'\n]\n```\n\n### Example 3 - Iterable\n\n```js\nnew Headers({\n  'content-length': '123',\n  'content-type': 'text/plain',\n  connection: 'keep-alive',\n  host: 'mysite.com',\n  accept: '*/*'\n})\n```\nor\n```js\nnew Map([\n  ['content-length', '123'],\n  ['content-type', 'text/plain'],\n  ['connection', 'keep-alive'],\n  ['host', 'mysite.com'],\n  ['accept', '*/*']\n])\n```\nor\n```js\n{\n  *[Symbol.iterator] () {\n    yield ['content-length', '123']\n    yield ['content-type', 'text/plain']\n    yield ['connection', 'keep-alive']\n    yield ['host', 'mysite.com']\n    yield ['accept', '*/*']\n  }\n}\n```\n"
  },
  {
    "path": "docs/docs/api/EnvHttpProxyAgent.md",
    "content": "# Class: EnvHttpProxyAgent\n\nExtends: `undici.Dispatcher`\n\nEnvHttpProxyAgent automatically reads the proxy configuration from the environment variables `http_proxy`, `https_proxy`, and `no_proxy` and sets up the proxy agents accordingly. When `http_proxy` and `https_proxy` are set, `http_proxy` is used for HTTP requests and `https_proxy` is used for HTTPS requests. If only `http_proxy` is set, `http_proxy` is used for both HTTP and HTTPS requests. If only `https_proxy` is set, it is only used for HTTPS requests.\n\n`no_proxy` is a comma or space-separated list of hostnames that should not be proxied. The list may contain leading wildcard characters (`*`). If `no_proxy` is set, the EnvHttpProxyAgent will bypass the proxy for requests to hosts that match the list. If `no_proxy` is set to `\"*\"`, the EnvHttpProxyAgent will bypass the proxy for all requests.\n\nUppercase environment variables are also supported: `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. However, if both the lowercase and uppercase environment variables are set, the uppercase environment variables will be ignored.\n\n## `new EnvHttpProxyAgent([options])`\n\nArguments:\n\n* **options** `EnvHttpProxyAgentOptions` (optional) - extends the `Agent` options.\n\nReturns: `EnvHttpProxyAgent`\n\n### Parameter: `EnvHttpProxyAgentOptions`\n\nExtends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)\n\n* **httpProxy** `string` (optional) - When set, it will override the `HTTP_PROXY` environment variable.\n* **httpsProxy** `string` (optional) - When set, it will override the `HTTPS_PROXY` environment variable.\n* **noProxy** `string` (optional) - When set, it will override the `NO_PROXY` environment variable.\n\nExamples:\n\n```js\nimport { EnvHttpProxyAgent } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\n// or\nconst envHttpProxyAgent = new EnvHttpProxyAgent({ httpProxy: 'my.proxy.server:8080', httpsProxy: 'my.proxy.server:8443', noProxy: 'localhost' })\n```\n\n#### Example - EnvHttpProxyAgent instantiation\n\nThis will instantiate the EnvHttpProxyAgent. It will not do anything until registered as the agent to use with requests.\n\n```js\nimport { EnvHttpProxyAgent } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\n```\n\n#### Example - Basic Proxy Fetch with global agent dispatcher\n\n```js\nimport { setGlobalDispatcher, fetch, EnvHttpProxyAgent } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\nsetGlobalDispatcher(envHttpProxyAgent)\n\nconst { status, json } = await fetch('http://localhost:3000/foo')\n\nconsole.log('response received', status) // response received 200\n\nconst data = await json() // data { foo: \"bar\" }\n```\n\n#### Example - Basic Proxy Request with global agent dispatcher\n\n```js\nimport { setGlobalDispatcher, request, EnvHttpProxyAgent } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\nsetGlobalDispatcher(envHttpProxyAgent)\n\nconst { statusCode, body } = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Proxy Request with local agent dispatcher\n\n```js\nimport { EnvHttpProxyAgent, request } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Proxy Fetch with local agent dispatcher\n\n```js\nimport { EnvHttpProxyAgent, fetch } from 'undici'\n\nconst envHttpProxyAgent = new EnvHttpProxyAgent()\n\nconst {\n  status,\n  json\n} = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent })\n\nconsole.log('response received', status) // response received 200\n\nconst data = await json() // data { foo: \"bar\" }\n```\n\n## Instance Methods\n\n### `EnvHttpProxyAgent.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `EnvHttpProxyAgent.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `EnvHttpProxyAgent.dispatch(options, handler: AgentDispatchOptions)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n#### Parameter: `AgentDispatchOptions`\n\nExtends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions)\n\n* **origin** `string | URL`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `EnvHttpProxyAgent.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `EnvHttpProxyAgent.dispatch(options, handler)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `EnvHttpProxyAgent.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `EnvHttpProxyAgent.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `EnvHttpProxyAgent.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `EnvHttpProxyAgent.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n"
  },
  {
    "path": "docs/docs/api/Errors.md",
    "content": "# Errors\n\nUndici exposes a variety of error objects that you can use to enhance your error handling.\nYou can find all the error objects inside the `errors` key.\n\n```js\nimport { errors } from 'undici'\n```\n\n| Error                                | Error Codes                           | Description                                                               |\n| ------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------- |\n| `UndiciError`                        | `UND_ERR`                             | all errors below are extended from `UndiciError`.                         |\n| `ConnectTimeoutError`                | `UND_ERR_CONNECT_TIMEOUT`             | socket is destroyed due to connect timeout.                               |\n| `HeadersTimeoutError`                | `UND_ERR_HEADERS_TIMEOUT`             | socket is destroyed due to headers timeout.                               |\n| `HeadersOverflowError`               | `UND_ERR_HEADERS_OVERFLOW`            | socket is destroyed due to headers' max size being exceeded.              |\n| `BodyTimeoutError`                   | `UND_ERR_BODY_TIMEOUT`                | socket is destroyed due to body timeout.                                  |\n| `InvalidArgumentError`               | `UND_ERR_INVALID_ARG`                 | passed an invalid argument.                                               |\n| `InvalidReturnValueError`            | `UND_ERR_INVALID_RETURN_VALUE`        | returned an invalid value.                                                |\n| `RequestAbortedError`                | `UND_ERR_ABORTED`                     | the request has been aborted by the user                                  |\n| `ClientDestroyedError`               | `UND_ERR_DESTROYED`                   | trying to use a destroyed client.                                         |\n| `ClientClosedError`                  | `UND_ERR_CLOSED`                      | trying to use a closed client.                                            |\n| `SocketError`                        | `UND_ERR_SOCKET`                      | there is an error with the socket.                                        |\n| `NotSupportedError`                  | `UND_ERR_NOT_SUPPORTED`               | encountered unsupported functionality.                                    |\n| `RequestContentLengthMismatchError`  | `UND_ERR_REQ_CONTENT_LENGTH_MISMATCH` | request body does not match content-length header                         |\n| `ResponseContentLengthMismatchError` | `UND_ERR_RES_CONTENT_LENGTH_MISMATCH` | response body does not match content-length header                        |\n| `InformationalError`                 | `UND_ERR_INFO`                        | expected error with reason                                                |\n| `ResponseExceededMaxSizeError`       | `UND_ERR_RES_EXCEEDED_MAX_SIZE`       | response body exceed the max size allowed                                 |\n| `SecureProxyConnectionError`         | `UND_ERR_PRX_TLS`                     | tls connection to a proxy failed                                          |\n| `MessageSizeExceededError`           | `UND_ERR_WS_MESSAGE_SIZE_EXCEEDED`    | WebSocket decompressed message exceeded the maximum allowed size          |\n\nBe aware of the possible difference between the global dispatcher version and the actual undici version you might be using. We recommend to avoid the check `instanceof errors.UndiciError` and seek for the `error.code === '<error_code>'` instead to avoid inconsistencies.\n### `SocketError`\n\nThe `SocketError` has a `.socket` property which holds socket metadata:\n\n```ts\ninterface SocketInfo {\n  localAddress?: string\n  localPort?: number\n  remoteAddress?: string\n  remotePort?: number\n  remoteFamily?: string\n  timeout?: number\n  bytesWritten?: number\n  bytesRead?: number\n}\n```\n\nBe aware that in some cases the `.socket` property can be `null`.\n"
  },
  {
    "path": "docs/docs/api/EventSource.md",
    "content": "# EventSource\n\n> ⚠️ Warning: the EventSource API is experimental.\n\nUndici exposes a WHATWG spec-compliant implementation of [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource)\nfor [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events).\n\n## Instantiating EventSource\n\nUndici exports a EventSource class. You can instantiate the EventSource as\nfollows:\n\n```mjs\nimport { EventSource } from 'undici'\n\nconst eventSource = new EventSource('http://localhost:3000')\neventSource.onmessage = (event) => {\n  console.log(event.data)\n}\n```\n\n## Using a custom Dispatcher\n\nundici allows you to set your own Dispatcher in the EventSource constructor.\n\nAn example which allows you to modify the request headers is:\n\n```mjs\nimport { EventSource, Agent } from 'undici'\n\nclass CustomHeaderAgent extends Agent {\n  dispatch (opts) {\n    opts.headers['x-custom-header'] = 'hello world'\n    return super.dispatch(...arguments)\n  }\n}\n\nconst eventSource = new EventSource('http://localhost:3000', {\n  dispatcher: new CustomHeaderAgent()\n})\n\n```\n\nMore information about the EventSource API can be found on\n[MDN](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).\n"
  },
  {
    "path": "docs/docs/api/Fetch.md",
    "content": "# Fetch\n\nUndici exposes a fetch() method starts the process of fetching a resource from the network.\n\nDocumentation and examples can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch).\n\n## FormData\n\nThis API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/FormData).\n\nIf any parameters are passed to the FormData constructor other than `undefined`, an error will be thrown. Other parameters are ignored.\n\n## Response\n\nThis API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)\n\n## Request\n\nThis API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n\n## Header\n\nThis API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)\n\n# Body Mixins\n\n`Response` and `Request` body inherit body mixin methods. These methods include:\n\n- [`.arrayBuffer()`](https://fetch.spec.whatwg.org/#dom-body-arraybuffer)\n- [`.blob()`](https://fetch.spec.whatwg.org/#dom-body-blob)\n- [`.bytes()`](https://fetch.spec.whatwg.org/#dom-body-bytes)\n- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)\n- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)\n- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)\n\nThere is an ongoing discussion regarding `.formData()` and its usefulness and performance in server environments. It is recommended to use a dedicated library for parsing `multipart/form-data` bodies, such as [Busboy](https://www.npmjs.com/package/busboy) or [@fastify/busboy](https://www.npmjs.com/package/@fastify/busboy).\n\nThese libraries can be interfaced with fetch with the following example code:\n\n```mjs\nimport { Busboy } from '@fastify/busboy'\nimport { Readable } from 'node:stream'\n\nconst response = await fetch('...')\nconst busboy = new Busboy({\n  headers: {\n    'content-type': response.headers.get('content-type')\n  }\n})\n\nReadable.fromWeb(response.body).pipe(busboy)\n```\n"
  },
  {
    "path": "docs/docs/api/GlobalInstallation.md",
    "content": "# Global Installation\n\nUndici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally without requiring imports.\n\n## `install()`\n\nInstall all WHATWG fetch classes globally on `globalThis`.\n\n**Example:**\n\n```js\nimport { install } from 'undici'\n\n// Install all WHATWG fetch classes globally  \ninstall()\n\n// Now you can use fetch classes globally without importing\nconst response = await fetch('https://api.example.com/data')\nconst data = await response.json()\n\n// All classes are available globally:\nconst headers = new Headers([['content-type', 'application/json']])\nconst request = new Request('https://example.com')\nconst formData = new FormData()\nconst ws = new WebSocket('wss://example.com')\nconst eventSource = new EventSource('https://example.com/events')\n```\n\n## Installed Classes\n\nThe `install()` function adds the following classes to `globalThis`:\n\n| Class | Description |\n|-------|-------------|\n| `fetch` | The fetch function for making HTTP requests |\n| `Headers` | HTTP headers management |\n| `Response` | HTTP response representation |\n| `Request` | HTTP request representation |\n| `FormData` | Form data handling |\n| `WebSocket` | WebSocket client |\n| `CloseEvent` | WebSocket close event |\n| `ErrorEvent` | WebSocket error event |\n| `MessageEvent` | WebSocket message event |\n| `EventSource` | Server-sent events client |\n\n## Use Cases\n\nGlobal installation is useful for:\n\n- **Polyfilling environments** that don't have native fetch support\n- **Ensuring consistent behavior** across different Node.js versions\n- **Library compatibility** when third-party libraries expect global fetch\n- **Migration scenarios** where you want to replace built-in implementations\n- **Testing environments** where you need predictable fetch behavior\n\n## Example: Polyfilling an Environment\n\n```js\nimport { install } from 'undici'\n\n// Check if fetch is available and install if needed\nif (typeof globalThis.fetch === 'undefined') {\n  install()\n  console.log('Undici fetch installed globally')\n}\n\n// Now fetch is guaranteed to be available\nconst response = await fetch('https://api.example.com')\n```\n\n## Example: Testing Environment\n\n```js\nimport { install } from 'undici'\n\n// In test setup, ensure consistent fetch behavior\ninstall()\n\n// Now all tests use undici's implementations\ntest('fetch API test', async () => {\n  const response = await fetch('https://example.com')\n  expect(response).toBeInstanceOf(Response)\n})\n```\n\n## Notes\n\n- The `install()` function overwrites any existing global implementations\n- Classes installed are undici's implementations, not Node.js built-ins\n- This provides access to undici's latest features and performance improvements\n- The global installation persists for the lifetime of the process"
  },
  {
    "path": "docs/docs/api/H2CClient.md",
    "content": "# Class: H2CClient\n\nExtends: `undici.Dispatcher`\n\nA basic H2C client.\n\n**Example**\n\n```js\nconst { createServer } = require('node:http2')\nconst { once } = require('node:events')\nconst { H2CClient } = require('undici')\n\nconst server = createServer((req, res) => {\n  res.writeHead(200)\n  res.end('Hello, world!')\n})\n\nserver.listen()\nonce(server, 'listening').then(() => {\n  const client = new H2CClient(`http://localhost:${server.address().port}/`)\n\n  const response = await client.request({ path: '/', method: 'GET' })\n  console.log(response.statusCode) // 200\n  response.body.text.then((text) => {\n    console.log(text) // Hello, world!\n  })\n})\n```\n\n## `new H2CClient(url[, options])`\n\nArguments:\n\n- **url** `URL | string` - Should only include the **protocol, hostname, and port**. It only supports `http` protocol.\n- **options** `H2CClientOptions` (optional)\n\nReturns: `H2CClient`\n\n### Parameter: `H2CClientOptions`\n\n- **bodyTimeout** `number | null` (optional) - Default: `300e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds. Please note the `timeout` will be reset if you keep writing data to the socket everytime.\n- **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.\n- **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by _keep-alive_ hints from the server. Defaults to 10 minutes.\n- **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by _keep-alive_ hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.\n- **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server _keep-alive_ hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.\n- **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.\n- **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.\n- **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.\n- **pipelining** `number | null` (optional) - Default to `maxConcurrentStreams` - The amount of concurrent requests sent over a single HTTP/2 session in accordance with [RFC-7540](https://httpwg.org/specs/rfc7540.html#StreamsLayer) Stream specification. Streams can be closed up by remote server at any time.\n- **pingInterval**: `number` - Default: `60e3`. The time interval in milliseconds between PING frames sent to the server. Set to `0` to disable PING frames. This is only applicable for HTTP/2 connections.\n- **connect** `ConnectOptions | null` (optional) - Default: `null`.\n- **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source.\n- **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.\n- **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.\n\n#### Parameter: `H2CConnectOptions`\n\n- **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.\n- **timeout** `number | null` (optional) - In milliseconds, Default `10e3`.\n- **servername** `string | null` (optional)\n- **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled\n- **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds\n\n### Example - Basic Client instantiation\n\nThis will instantiate the undici H2CClient, but it will not connect to the origin until something is queued. Consider using `client.connect` to prematurely connect to the origin, or just call `client.request`.\n\n```js\n\"use strict\";\nimport { H2CClient } from \"undici\";\n\nconst client = new H2CClient(\"http://localhost:3000\");\n```\n\n## Instance Methods\n\n### `H2CClient.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `H2CClient.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\nWaits until socket is closed before invoking the callback (or returning a promise if no callback is provided).\n\n### `H2CClient.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `H2CClient.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `H2CClient.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `H2CClient.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `H2CClient.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `H2CClient.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n## Instance Properties\n\n### `H2CClient.closed`\n\n- `boolean`\n\n`true` after `H2CClient.close()` has been called.\n\n### `H2CClient.destroyed`\n\n- `boolean`\n\n`true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed.\n\n### `H2CClient.pipelining`\n\n- `number`\n\nProperty to get and set the pipelining factor.\n\n## Instance Events\n\n### Event: `'connect'`\n\nSee [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).\n\nParameters:\n\n- **origin** `URL`\n- **targets** `Array<Dispatcher>`\n\nEmitted when a socket has been created and connected. The client will connect once `client.size > 0`.\n\n#### Example - Client connect event\n\n```js\nimport { createServer } from \"node:http2\";\nimport { H2CClient } from \"undici\";\nimport { once } from \"events\";\n\nconst server = createServer((request, response) => {\n  response.end(\"Hello, World!\");\n}).listen();\n\nawait once(server, \"listening\");\n\nconst client = new H2CClient(`http://localhost:${server.address().port}`);\n\nclient.on(\"connect\", (origin) => {\n  console.log(`Connected to ${origin}`); // should print before the request body statement\n});\n\ntry {\n  const { body } = await client.request({\n    path: \"/\",\n    method: \"GET\",\n  });\n  body.setEncoding(\"utf-8\");\n  body.on(\"data\", console.log);\n  client.close();\n  server.close();\n} catch (error) {\n  console.error(error);\n  client.close();\n  server.close();\n}\n```\n\n### Event: `'disconnect'`\n\nSee [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).\n\nParameters:\n\n- **origin** `URL`\n- **targets** `Array<Dispatcher>`\n- **error** `Error`\n\nEmitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`.\n\n#### Example - Client disconnect event\n\n```js\nimport { createServer } from \"node:http2\";\nimport { H2CClient } from \"undici\";\nimport { once } from \"events\";\n\nconst server = createServer((request, response) => {\n  response.destroy();\n}).listen();\n\nawait once(server, \"listening\");\n\nconst client = new H2CClient(`http://localhost:${server.address().port}`);\n\nclient.on(\"disconnect\", (origin) => {\n  console.log(`Disconnected from ${origin}`);\n});\n\ntry {\n  await client.request({\n    path: \"/\",\n    method: \"GET\",\n  });\n} catch (error) {\n  console.error(error.message);\n  client.close();\n  server.close();\n}\n```\n\n### Event: `'drain'`\n\nEmitted when pipeline is no longer busy.\n\nSee [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).\n\n#### Example - Client drain event\n\n```js\nimport { createServer } from \"node:http2\";\nimport { H2CClient } from \"undici\";\nimport { once } from \"events\";\n\nconst server = createServer((request, response) => {\n  response.end(\"Hello, World!\");\n}).listen();\n\nawait once(server, \"listening\");\n\nconst client = new H2CClient(`http://localhost:${server.address().port}`);\n\nclient.on(\"drain\", () => {\n  console.log(\"drain event\");\n  client.close();\n  server.close();\n});\n\nconst requests = [\n  client.request({ path: \"/\", method: \"GET\" }),\n  client.request({ path: \"/\", method: \"GET\" }),\n  client.request({ path: \"/\", method: \"GET\" }),\n];\n\nawait Promise.all(requests);\n\nconsole.log(\"requests completed\");\n```\n\n### Event: `'error'`\n\nInvoked for users errors such as throwing in the `onError` handler.\n"
  },
  {
    "path": "docs/docs/api/MockAgent.md",
    "content": "# Class: MockAgent\n\nExtends: `undici.Dispatcher`\n\nA mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead.\n\n## `new MockAgent([options])`\n\nArguments:\n\n* **options** `MockAgentOptions` (optional) - It extends the `Agent` options.\n\nReturns: `MockAgent`\n\n### Parameter: `MockAgentOptions`\n\nExtends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)\n\n* **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent.\n\n* **ignoreTrailingSlash** `boolean` (optional) - Default: `false` - set the default value for `ignoreTrailingSlash` for interceptors.\n\n* **acceptNonStandardSearchParameters** `boolean` (optional) - Default: `false` - set to `true` if the matcher should also accept non standard search parameters such as multi-value items specified with `[]` (e.g. `param[]=1&param[]=2&param[]=3`) and multi-value items which values are comma separated (e.g. `param=1,2,3`).\n\n### Example - Basic MockAgent instantiation\n\nThis will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added.\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent()\n```\n\n### Example - Basic MockAgent instantiation with custom agent\n\n```js\nimport { Agent, MockAgent } from 'undici'\n\nconst agent = new Agent()\n\nconst mockAgent = new MockAgent({ agent })\n```\n\n## Instance Methods\n\n### `MockAgent.get(origin)`\n\nThis method creates and retrieves MockPool or MockClient instances which can then be used to intercept HTTP requests. If the number of connections on the mock agent is set to 1, a MockClient instance is returned. Otherwise a MockPool instance is returned.\n\nFor subsequent `MockAgent.get` calls on the same origin, the same mock instance will be returned.\n\nArguments:\n\n* **origin** `string | RegExp | (value) => boolean` - a matcher for the pool origin to be retrieved from the MockAgent.\n\n| Matcher type | Condition to pass          |\n|:------------:| -------------------------- |\n| `string`     | Exact match against string |\n| `RegExp`     | Regex must pass            |\n| `Function`   | Function must return true  |\n\nReturns: `MockClient | MockPool`.\n\n| `MockAgentOptions`   | Mock instance returned |\n| -------------------- | ---------------------- |\n| `connections === 1`  | `MockClient`           |\n| `connections` > `1`  | `MockPool`             |\n\n#### Example - Basic Mocked Request\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst { statusCode, body } = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Mocked Request with local mock agent dispatcher\n\n```js\nimport { MockAgent, request } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: mockAgent })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Mocked Request with local mock pool dispatcher\n\n```js\nimport { MockAgent, request } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: mockPool })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Mocked Request with local mock client dispatcher\n\n```js\nimport { MockAgent, request } from 'undici'\n\nconst mockAgent = new MockAgent({ connections: 1 })\n\nconst mockClient = mockAgent.get('http://localhost:3000')\nmockClient.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: mockClient })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Mocked requests with multiple intercepts\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\nmockPool.intercept({ path: '/hello'}).reply(200, 'hello')\n\nconst result1 = await request('http://localhost:3000/foo')\n\nconsole.log('response received', result1.statusCode) // response received 200\n\nfor await (const data of result1.body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n\nconst result2 = await request('http://localhost:3000/hello')\n\nconsole.log('response received', result2.statusCode) // response received 200\n\nfor await (const data of result2.body) {\n  console.log('data', data.toString('utf8')) // data hello\n}\n```\n\n#### Example - Mock different requests within the same file\n\n```js\nconst { MockAgent, setGlobalDispatcher } = require('undici');\nconst agent = new MockAgent();\nagent.disableNetConnect();\nsetGlobalDispatcher(agent);\ndescribe('Test', () => {\n  it('200', async () => {\n    const mockAgent = agent.get('http://test.com');\n    // your test\n  });\n  it('200', async () => {\n    const mockAgent = agent.get('http://testing.com');\n    // your test\n  });\n});\n```\n\n#### Example - Mocked request with query body, headers and trailers\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo?hello=there&see=ya',\n  method: 'POST',\n  body: 'form1=data1&form2=data2'\n}).reply(200, { foo: 'bar' }, {\n  headers: { 'content-type': 'application/json' },\n  trailers: { 'Content-MD5': 'test' }\n})\n\nconst {\n  statusCode,\n  headers,\n  trailers,\n  body\n} = await request('http://localhost:3000/foo?hello=there&see=ya', {\n  method: 'POST',\n  body: 'form1=data1&form2=data2'\n})\n\nconsole.log('response received', statusCode) // response received 200\nconsole.log('headers', headers) // { 'content-type': 'application/json' }\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // '{\"foo\":\"bar\"}'\n}\n\nconsole.log('trailers', trailers) // { 'content-md5': 'test' }\n```\n\n#### Example - Mocked request with origin regex\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get(new RegExp('http://localhost:3000'))\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Mocked request with origin function\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get((origin) => origin === 'http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n### `MockAgent.close()`\n\nCloses the mock agent and waits for registered mock pools and clients to also close before resolving.\n\nReturns: `Promise<void>`\n\n#### Example - clean up after tests are complete\n\n```js\nimport { MockAgent, setGlobalDispatcher } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nawait mockAgent.close()\n```\n\n### `MockAgent.dispatch(options, handlers)`\n\nImplements [`Agent.dispatch(options, handlers)`](/docs/docs/api/Agent.md#parameter-agentdispatchoptions).\n\n### `MockAgent.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n#### Example - MockAgent request\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await mockAgent.request({\n  origin: 'http://localhost:3000',\n  path: '/foo',\n  method: 'GET'\n})\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n### `MockAgent.deactivate()`\n\nThis method disables mocking in MockAgent.\n\nReturns: `void`\n\n#### Example - Deactivate Mocking\n\n```js\nimport { MockAgent, setGlobalDispatcher } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.deactivate()\n```\n\n### `MockAgent.activate()`\n\nThis method enables mocking in a MockAgent instance. When instantiated, a MockAgent is automatically activated. Therefore, this method is only effective after `MockAgent.deactivate` has been called.\n\nReturns: `void`\n\n#### Example - Activate Mocking\n\n```js\nimport { MockAgent, setGlobalDispatcher } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.deactivate()\n// No mocking will occur\n\n// Later\nmockAgent.activate()\n```\n\n### `MockAgent.enableNetConnect([host])`\n\nWhen requests are not matched in a MockAgent intercept, a real HTTP request is attempted. We can control this further through the use of `enableNetConnect`. This is achieved by defining host matchers so only matching requests will be attempted.\n\nWhen using a string, it should only include the **hostname and optionally, the port**. In addition, calling this method multiple times with a string will allow all HTTP requests that match these values.\n\nArguments:\n\n* **host** `string | RegExp | (value) => boolean` - (optional)\n\nReturns: `void`\n\n#### Example - Allow all non-matching urls to be dispatched in a real HTTP request\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.enableNetConnect()\n\nawait request('http://example.com')\n// A real request is made\n```\n\n#### Example - Allow requests matching a host string to make real requests\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.enableNetConnect('example-1.com')\nmockAgent.enableNetConnect('example-2.com:8080')\n\nawait request('http://example-1.com')\n// A real request is made\n\nawait request('http://example-2.com:8080')\n// A real request is made\n\nawait request('http://example-3.com')\n// Will throw\n```\n\n#### Example - Allow requests matching a host regex to make real requests\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.enableNetConnect(new RegExp('example.com'))\n\nawait request('http://example.com')\n// A real request is made\n```\n\n#### Example - Allow requests matching a host function to make real requests\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nmockAgent.enableNetConnect((value) => value === 'example.com')\n\nawait request('http://example.com')\n// A real request is made\n```\n\n### `MockAgent.disableNetConnect()`\n\nThis method causes all requests to throw when requests are not matched in a MockAgent intercept.\n\nReturns: `void`\n\n#### Example - Disable all non-matching requests by throwing an error for each\n\n```js\nimport { MockAgent, request } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nmockAgent.disableNetConnect()\n\nawait request('http://example.com')\n// Will throw\n```\n\n### `MockAgent.pendingInterceptors()`\n\nThis method returns any pending interceptors registered on a mock agent. A pending interceptor meets one of the following criteria:\n\n- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked;\n- Is persistent (i.e., registered with `.persist()`) and has not been invoked;\n- Is registered with `.times(<number>)` and has not been invoked `<number>` of times.\n\nReturns: `PendingInterceptor[]` (where `PendingInterceptor` is a `MockDispatch` with an additional `origin: string`)\n\n#### Example - List all pending interceptors\n\n```js\nconst agent = new MockAgent()\nagent.disableNetConnect()\n\nagent\n  .get('https://example.com')\n  .intercept({ method: 'GET', path: '/' })\n  .reply(200)\n\nconst pendingInterceptors = agent.pendingInterceptors()\n// Returns [\n//   {\n//     timesInvoked: 0,\n//     times: 1,\n//     persist: false,\n//     consumed: false,\n//     pending: true,\n//     path: '/',\n//     method: 'GET',\n//     body: undefined,\n//     headers: undefined,\n//     data: {\n//       error: null,\n//       statusCode: 200,\n//       data: '',\n//       headers: {},\n//       trailers: {}\n//     },\n//     origin: 'https://example.com'\n//   }\n// ]\n```\n\n### `MockAgent.assertNoPendingInterceptors([options])`\n\nThis method throws if the mock agent has any pending interceptors. A pending interceptor meets one of the following criteria:\n\n- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked;\n- Is persistent (i.e., registered with `.persist()`) and has not been invoked;\n- Is registered with `.times(<number>)` and has not been invoked `<number>` of times.\n\n#### Example - Check that there are no pending interceptors\n\n```js\nconst agent = new MockAgent()\nagent.disableNetConnect()\n\nagent\n  .get('https://example.com')\n  .intercept({ method: 'GET', path: '/' })\n  .reply(200)\n\nagent.assertNoPendingInterceptors()\n// Throws an UndiciError with the following message:\n//\n// 1 interceptor is pending:\n//\n// ┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐\n// │ (index) │ Method │        Origin         │ Path │ Status code │ Persistent │ Invocations │ Remaining │\n// ├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤\n// │    0    │ 'GET'  │ 'https://example.com' │ '/'  │     200     │    '❌'    │      0      │     1     │\n// └─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘\n```\n\n#### Example - access call history on MockAgent\n\nYou can register every call made within a MockAgent to be able to retrieve the body, headers and so on.\n\nThis is not enabled by default.\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent({ enableCallHistory: true })\nsetGlobalDispatcher(mockAgent)\n\nawait request('http://example.com', { query: { item: 1 }})\n\nmockAgent.getCallHistory()?.firstCall()\n// Returns\n// MockCallHistoryLog {\n//   body: undefined,\n//   headers: undefined,\n//   method: 'GET',\n//   origin: 'http://example.com',\n//   fullUrl: 'http://example.com/?item=1',\n//   path: '/',\n//   searchParams: { item: '1' },\n//   protocol: 'http:',\n//   host: 'example.com',\n//   port: ''\n// }\n```\n\n#### Example - clear call history\n\n```js\nconst mockAgent = new MockAgent()\n\nmockAgent.clearAllCallHistory()\n```\n\n#### Example - call history instance class method\n\n```js\nconst mockAgent = new MockAgent()\n\nconst mockAgentHistory = mockAgent.getCallHistory()\n\nmockAgentHistory?.calls() // returns an array of MockCallHistoryLogs\nmockAgentHistory?.firstCall() // returns the first MockCallHistoryLogs or undefined\nmockAgentHistory?.lastCall() // returns the last MockCallHistoryLogs or undefined\nmockAgentHistory?.nthCall(3) // returns the third MockCallHistoryLogs or undefined\nmockAgentHistory?.filterCalls({ path: '/endpoint', hash: '#hash-value' }) // returns an Array of MockCallHistoryLogs WHERE path === /endpoint OR hash === #hash-value\nmockAgentHistory?.filterCalls({ path: '/endpoint', hash: '#hash-value' }, { operator: 'AND' }) // returns an Array of MockCallHistoryLogs WHERE path === /endpoint AND hash === #hash-value\nmockAgentHistory?.filterCalls(/\"data\": \"{}\"/) // returns an Array of MockCallHistoryLogs where any value match regexp\nmockAgentHistory?.filterCalls('application/json') // returns an Array of MockCallHistoryLogs where any value === 'application/json'\nmockAgentHistory?.filterCalls((log) => log.path === '/endpoint') // returns an Array of MockCallHistoryLogs when given function returns true\nmockAgentHistory?.clear() // clear the history\n```\n"
  },
  {
    "path": "docs/docs/api/MockCallHistory.md",
    "content": "# Class: MockCallHistory\n\nAccess to an instance with :\n\n```js\nconst mockAgent = new MockAgent({ enableCallHistory: true })\nmockAgent.getCallHistory()\n\n// or\nconst mockAgent = new MockAgent()\nmockAgent.enableMockHistory()\nmockAgent.getCallHistory()\n\n```\n\na MockCallHistory instance implements a **Symbol.iterator** letting you iterate on registered logs :\n\n```ts\nfor (const log of mockAgent.getCallHistory()) {\n  //...\n}\n\nconst array: Array<MockCallHistoryLog> = [...mockAgent.getCallHistory()]\nconst set: Set<MockCallHistoryLog> = new Set(mockAgent.getCallHistory())\n```\n\n## class methods\n\n### clear\n\nClear all MockCallHistoryLog registered. This is automatically done when calling `mockAgent.close()`\n\n```js\nmockAgent.clearCallHistory()\n// same as\nmockAgent.getCallHistory()?.clear()\n```\n\n### calls\n\nGet all MockCallHistoryLog registered as an array\n\n```js\nmockAgent.getCallHistory()?.calls()\n```\n\n### firstCall\n\nGet the first MockCallHistoryLog registered or undefined\n\n```js\nmockAgent.getCallHistory()?.firstCall()\n```\n\n### lastCall\n\nGet the last MockCallHistoryLog registered or undefined\n\n```js\nmockAgent.getCallHistory()?.lastCall()\n```\n\n### nthCall\n\nGet the nth MockCallHistoryLog registered or undefined\n\n```js\nmockAgent.getCallHistory()?.nthCall(3) // the third MockCallHistoryLog registered\n```\n\n### filterCallsByProtocol\n\nFilter MockCallHistoryLog by protocol.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByProtocol(/https/)\nmockAgent.getCallHistory()?.filterCallsByProtocol('https:')\n```\n\n### filterCallsByHost\n\nFilter MockCallHistoryLog by host.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByHost(/localhost/)\nmockAgent.getCallHistory()?.filterCallsByHost('localhost:3000')\n```\n\n### filterCallsByPort\n\nFilter MockCallHistoryLog by port.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByPort(/3000/)\nmockAgent.getCallHistory()?.filterCallsByPort('3000')\nmockAgent.getCallHistory()?.filterCallsByPort('')\n```\n\n### filterCallsByOrigin\n\nFilter MockCallHistoryLog by origin.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByOrigin(/http:\\/\\/localhost:3000/)\nmockAgent.getCallHistory()?.filterCallsByOrigin('http://localhost:3000')\n```\n\n### filterCallsByPath\n\nFilter MockCallHistoryLog by path.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByPath(/api\\/v1\\/graphql/)\nmockAgent.getCallHistory()?.filterCallsByPath('/api/v1/graphql')\n```\n\n### filterCallsByHash\n\nFilter MockCallHistoryLog by hash.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByPath(/hash/)\nmockAgent.getCallHistory()?.filterCallsByPath('#hash')\n```\n\n### filterCallsByFullUrl\n\nFilter MockCallHistoryLog by fullUrl. fullUrl contains protocol, host, port, path, hash, and query params\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByFullUrl(/https:\\/\\/localhost:3000\\/\\?query=value#hash/)\nmockAgent.getCallHistory()?.filterCallsByFullUrl('https://localhost:3000/?query=value#hash')\n```\n\n### filterCallsByMethod\n\nFilter MockCallHistoryLog by method.\n\n> more details for the first parameter can be found [here](/docs/docs/api/MockCallHistory.md#filter-parameter)\n\n```js\nmockAgent.getCallHistory()?.filterCallsByMethod(/POST/)\nmockAgent.getCallHistory()?.filterCallsByMethod('POST')\n```\n\n### filterCalls\n\nThis class method is a meta function / alias to apply complex filtering in a single way.\n\nParameters :\n\n- criteria : the first parameter. a function, regexp or object.\n  - function : filter MockCallHistoryLog when the function returns false\n  - regexp : filter MockCallHistoryLog when the regexp does not match on MockCallHistoryLog.toString() ([see](./MockCallHistoryLog.md#to-string))\n  - object : an object with MockCallHistoryLog properties as keys to apply multiple filters. each values are a [filter parameter](/docs/docs/api/MockCallHistory.md#filter-parameter)\n- options : the second parameter. an object.\n  - options.operator : `'AND'` or `'OR'` (default `'OR'`). Used only if criteria is an object. see below\n\n```js\nmockAgent.getCallHistory()?.filterCalls((log) => log.hash === value && log.headers?.['authorization'] !== undefined)\nmockAgent.getCallHistory()?.filterCalls(/\"data\": \"{ \"errors\": \"wrong body\" }\"/)\n\n// returns an Array of MockCallHistoryLog which all have\n// - a hash containing my-hash\n// - OR\n// - a path equal to /endpoint\nmockAgent.getCallHistory()?.filterCalls({ hash: /my-hash/, path: '/endpoint' })\n\n// returns an Array of MockCallHistoryLog which all have\n// - a hash containing my-hash\n// - AND\n// - a path equal to /endpoint\nmockAgent.getCallHistory()?.filterCalls({ hash: /my-hash/, path: '/endpoint' }, { operator: 'AND' })\n```\n\n## filter parameter\n\nCan be :\n\n- string. MockCallHistoryLog filtered if `value !== parameterValue`\n- null. MockCallHistoryLog filtered if `value !== parameterValue`\n- undefined. MockCallHistoryLog filtered if `value !== parameterValue`\n- regexp. MockCallHistoryLog filtered if `!parameterValue.test(value)`\n"
  },
  {
    "path": "docs/docs/api/MockCallHistoryLog.md",
    "content": "# Class: MockCallHistoryLog\n\nAccess to an instance with :\n\n```js\nconst mockAgent = new MockAgent({ enableCallHistory: true })\nmockAgent.getCallHistory()?.firstCall()\n```\n\n## class properties\n\n- body `mockAgent.getCallHistory()?.firstCall()?.body`\n- headers `mockAgent.getCallHistory()?.firstCall()?.headers` an object\n- method `mockAgent.getCallHistory()?.firstCall()?.method` a string\n- fullUrl `mockAgent.getCallHistory()?.firstCall()?.fullUrl` a string containing the protocol, origin, path, query and hash\n- origin `mockAgent.getCallHistory()?.firstCall()?.origin` a string containing the protocol and the host\n- headers `mockAgent.getCallHistory()?.firstCall()?.headers` an object\n- path `mockAgent.getCallHistory()?.firstCall()?.path` a string always starting with `/`\n- searchParams `mockAgent.getCallHistory()?.firstCall()?.searchParams` an object\n- protocol `mockAgent.getCallHistory()?.firstCall()?.protocol` a string (`https:`)\n- host `mockAgent.getCallHistory()?.firstCall()?.host` a string\n- port `mockAgent.getCallHistory()?.firstCall()?.port` an empty string or a string containing numbers\n- hash `mockAgent.getCallHistory()?.firstCall()?.hash` an empty string or a string starting with `#`\n\n## class methods\n\n### toMap\n\nReturns a Map instance\n\n```js\nmockAgent.getCallHistory()?.firstCall()?.toMap()?.get('hash')\n// #hash\n```\n\n### toString\n\nReturns a string computed with any class property name and value pair\n\n```js\nmockAgent.getCallHistory()?.firstCall()?.toString()\n// protocol->https:|host->localhost:4000|port->4000|origin->https://localhost:4000|path->/endpoint|hash->#here|searchParams->{\"query\":\"value\"}|fullUrl->https://localhost:4000/endpoint?query=value#here|method->PUT|body->\"{ \"data\": \"hello\" }\"|headers->{\"content-type\":\"application/json\"}\n```\n"
  },
  {
    "path": "docs/docs/api/MockClient.md",
    "content": "# Class: MockClient\n\nExtends: `undici.Client`\n\nA mock client class that implements the same api as [MockPool](/docs/docs/api/MockPool.md).\n\n## `new MockClient(origin, [options])`\n\nArguments:\n\n* **origin** `string` - It should only include the **protocol, hostname, and port**.\n* **options** `MockClientOptions` - It extends the `Client` options.\n\nReturns: `MockClient`\n\n### Parameter: `MockClientOptions`\n\nExtends: `ClientOptions`\n\n* **agent** `Agent` - the agent to associate this MockClient with.\n\n### Example - Basic MockClient instantiation\n\nWe can use MockAgent to instantiate a MockClient ready to be used to intercept specified requests. It will not do anything until registered as the agent to use and any mock request are registered.\n\n```js\nimport { MockAgent } from 'undici'\n\n// Connections must be set to 1 to return a MockClient instance\nconst mockAgent = new MockAgent({ connections: 1 })\n\nconst mockClient = mockAgent.get('http://localhost:3000')\n```\n\n## Instance Methods\n\n### `MockClient.intercept(options)`\n\nImplements: [`MockPool.intercept(options)`](/docs/docs/api/MockPool.md#mockpoolinterceptoptions)\n\n### `MockClient.cleanMocks()`\n\nImplements: [`MockPool.cleanMocks()`](/docs/docs/api/MockPool.md#mockpoolcleanmocks)\n\n### `MockClient.close()`\n\nImplements: [`MockPool.close()`](/docs/docs/api/MockPool.md#mockpoolclose)\n\n### `MockClient.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `MockClient.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n#### Example - MockClient request\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent({ connections: 1 })\n\nconst mockClient = mockAgent.get('http://localhost:3000')\nmockClient.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await mockClient.request({\n  origin: 'http://localhost:3000',\n  path: '/foo',\n  method: 'GET'\n})\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n"
  },
  {
    "path": "docs/docs/api/MockErrors.md",
    "content": "# MockErrors\n\nUndici exposes a variety of mock error objects that you can use to enhance your mock error handling.\nYou can find all the mock error objects inside the `mockErrors` key.\n\n```js\nimport { mockErrors } from 'undici'\n```\n\n| Mock Error            | Mock Error Codes                | Description                                                |\n| --------------------- | ------------------------------- | ---------------------------------------------------------- |\n| `MockNotMatchedError` | `UND_MOCK_ERR_MOCK_NOT_MATCHED` | The request does not match any registered mock dispatches. |\n"
  },
  {
    "path": "docs/docs/api/MockPool.md",
    "content": "# Class: MockPool\n\nExtends: `undici.Pool`\n\nA mock Pool class that implements the Pool API and is used by MockAgent to intercept real requests and return mocked responses.\n\n## `new MockPool(origin, [options])`\n\nArguments:\n\n* **origin** `string` - It should only include the **protocol, hostname, and port**.\n* **options** `MockPoolOptions` - It extends the `Pool` options.\n\nReturns: `MockPool`\n\n### Parameter: `MockPoolOptions`\n\nExtends: `PoolOptions`\n\n* **agent** `Agent` - the agent to associate this MockPool with.\n\n### Example - Basic MockPool instantiation\n\nWe can use MockAgent to instantiate a MockPool ready to be used to intercept specified requests. It will not do anything until registered as the agent to use and any mock request are registered.\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n```\n\n## Instance Methods\n\n### `MockPool.intercept(options)`\n\nThis method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once. For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.\n\nWhen defining interception rules, all the rules must pass for a request to be intercepted. If a request is not intercepted, a real request will be attempted.\n\n| Matcher type | Condition to pass          |\n|:------------:| -------------------------- |\n| `string`     | Exact match against string |\n| `RegExp`     | Regex must pass            |\n| `Function`   | Function must return true  |\n\nArguments:\n\n* **options** `MockPoolInterceptOptions` - Interception options.\n\nReturns: `MockInterceptor` corresponding to the input options.\n\n### Parameter: `MockPoolInterceptOptions`\n\n* **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path. When a `RegExp` or callback is used, it will match against the request path including all query parameters in alphabetical order. When a `string` is provided, the query parameters can be conveniently specified through the `MockPoolInterceptOptions.query` setting.\n* **method** `string | RegExp | (method: string) => boolean` - (optional) - a matcher for the HTTP request method. Defaults to `GET`.\n* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.\n* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.\n* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. Only applies when a `string` was provided for `MockPoolInterceptOptions.path`.\n* **ignoreTrailingSlash** `boolean` - (optional) - set to `true` if the matcher should also match by ignoring potential trailing slashes in `MockPoolInterceptOptions.path`.\n\n### Return: `MockInterceptor`\n\nWe can define the behaviour of an intercepted request with the following options.\n\n* **reply** `(statusCode: number, replyData: string | Buffer | object | MockInterceptor.MockResponseDataHandler, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. You can define the replyData as a callback to read incoming request data. Default for `responseOptions` is `{}`.\n* **reply** `(callback: MockInterceptor.MockReplyOptionsCallback) => MockScope` - define a reply for a matching request, allowing dynamic mocking of all reply options rather than just the data.\n* **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw.\n* **defaultReplyHeaders** `(headers: Record<string, string>) => MockInterceptor` - define default headers to be included in subsequent replies. These are in addition to headers on a specific reply.\n* **defaultReplyTrailers** `(trailers: Record<string, string>) => MockInterceptor` - define default trailers to be included in subsequent replies. These are in addition to trailers on a specific reply.\n* **replyContentLength** `() => MockInterceptor` - define automatically calculated `content-length` headers to be included in subsequent replies.\n\nThe reply data of an intercepted request may either be a string, buffer, or JavaScript object. Objects are converted to JSON while strings and buffers are sent as-is.\n\nBy default, `reply` and `replyWithError` define the behaviour for the first matching request only. Subsequent requests will not be affected (this can be changed using the returned `MockScope`).\n\n### Parameter: `MockResponseOptions`\n\n* **headers** `Record<string, string>` - headers to be included on the mocked reply.\n* **trailers** `Record<string, string>` - trailers to be included on the mocked reply.\n\n### Return: `MockScope`\n\nA `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of an intercepted reply.\n\n* **delay** `(waitInMs: number) => MockScope` - delay the associated reply by a set amount in ms.\n* **persist** `() => MockScope` - any matching request will always reply with the defined response indefinitely.\n* **times** `(repeatTimes: number) => MockScope` - any matching request will reply with the defined response a fixed amount of times. This is overridden by **persist**.\n\n#### Example - Basic Mocked Request\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\n// MockPool\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({ path: '/foo' }).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Mocked request using reply data callbacks\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/echo',\n  method: 'GET',\n  headers: {\n    'User-Agent': 'undici',\n    Host: 'example.com'\n  }\n}).reply(200, ({ headers }) => ({ message: headers.get('message') }))\n\nconst { statusCode, body, headers } = await request('http://localhost:3000', {\n  headers: {\n    message: 'hello world!'\n  }\n})\n\nconsole.log('response received', statusCode) // response received 200\nconsole.log('headers', headers) // { 'content-type': 'application/json' }\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // { \"message\":\"hello world!\" }\n}\n```\n\n#### Example - Mocked request using reply options callback\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/echo',\n  method: 'GET',\n  headers: {\n    'User-Agent': 'undici',\n    Host: 'example.com'\n  }\n}).reply(({ headers }) => ({ statusCode: 200, data: { message: headers.get('message') }})))\n\nconst { statusCode, body, headers } = await request('http://localhost:3000', {\n  headers: {\n    message: 'hello world!'\n  }\n})\n\nconsole.log('response received', statusCode) // response received 200\nconsole.log('headers', headers) // { 'content-type': 'application/json' }\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // { \"message\":\"hello world!\" }\n}\n```\n\n#### Example - Basic Mocked requests with multiple intercepts\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).reply(200, 'foo')\n\nmockPool.intercept({\n  path: '/hello',\n  method: 'GET',\n}).reply(200, 'hello')\n\nconst result1 = await request('http://localhost:3000/foo')\n\nconsole.log('response received', result1.statusCode) // response received 200\n\nfor await (const data of result1.body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n\nconst result2 = await request('http://localhost:3000/hello')\n\nconsole.log('response received', result2.statusCode) // response received 200\n\nfor await (const data of result2.body) {\n  console.log('data', data.toString('utf8')) // data hello\n}\n```\n\n#### Example - Mocked request with query body, request headers and response headers and trailers\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo?hello=there&see=ya',\n  method: 'POST',\n  body: 'form1=data1&form2=data2',\n  headers: {\n    'User-Agent': 'undici',\n    Host: 'example.com'\n  }\n}).reply(200, { foo: 'bar' }, {\n  headers: { 'content-type': 'application/json' },\n  trailers: { 'Content-MD5': 'test' }\n})\n\nconst {\n  statusCode,\n  headers,\n  trailers,\n  body\n} = await request('http://localhost:3000/foo?hello=there&see=ya', {\n    method: 'POST',\n    body: 'form1=data1&form2=data2',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'example.com'\n    }\n  })\n\nconsole.log('response received', statusCode) // response received 200\nconsole.log('headers', headers) // { 'content-type': 'application/json' }\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // '{\"foo\":\"bar\"}'\n}\n\nconsole.log('trailers', trailers) // { 'content-md5': 'test' }\n```\n\n#### Example - Mocked request using different matchers\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: /^GET$/,\n  body: (value) => value === 'form=data',\n  headers: {\n    'User-Agent': 'undici',\n    Host: /^example.com$/\n  }\n}).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', {\n  method: 'GET',\n  body: 'form=data',\n  headers: {\n    foo: 'bar',\n    'User-Agent': 'undici',\n    Host: 'example.com'\n  }\n})\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Mocked request with reply with a defined error\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).replyWithError(new Error('kaboom'))\n\ntry {\n  await request('http://localhost:3000/foo', {\n    method: 'GET'\n  })\n} catch (error) {\n  console.error(error) // TypeError: fetch failed\n  console.error(error.cause) // Error: kaboom\n}\n```\n\n#### Example - Mocked request with defaultReplyHeaders\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).defaultReplyHeaders({ foo: 'bar' })\n  .reply(200, 'foo')\n\nconst { headers } = await request('http://localhost:3000/foo')\n\nconsole.log('headers', headers) // headers { foo: 'bar' }\n```\n\n#### Example - Mocked request with defaultReplyTrailers\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).defaultReplyTrailers({ foo: 'bar' })\n  .reply(200, 'foo')\n\nconst { trailers } = await request('http://localhost:3000/foo')\n\nconsole.log('trailers', trailers) // trailers { foo: 'bar' }\n```\n\n#### Example - Mocked request with automatic content-length calculation\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).replyContentLength().reply(200, 'foo')\n\nconst { headers } = await request('http://localhost:3000/foo')\n\nconsole.log('headers', headers) // headers { 'content-length': '3' }\n```\n\n#### Example - Mocked request with automatic content-length calculation on an object\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).replyContentLength().reply(200, { foo: 'bar' })\n\nconst { headers } = await request('http://localhost:3000/foo')\n\nconsole.log('headers', headers) // headers { 'content-length': '13' }\n```\n\n#### Example - Mocked request with persist enabled\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).reply(200, 'foo').persist()\n\nconst result1 = await request('http://localhost:3000/foo')\n// Will match and return mocked data\n\nconst result2 = await request('http://localhost:3000/foo')\n// Will match and return mocked data\n\n// Etc\n```\n\n#### Example - Mocked request with times enabled\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET'\n}).reply(200, 'foo').times(2)\n\nconst result1 = await request('http://localhost:3000/foo')\n// Will match and return mocked data\n\nconst result2 = await request('http://localhost:3000/foo')\n// Will match and return mocked data\n\nconst result3 = await request('http://localhost:3000/foo')\n// Will not match and make attempt a real request\n```\n\n#### Example - Mocked request with path callback\n\n```js\nimport { MockAgent, setGlobalDispatcher, request } from 'undici'\nimport querystring from 'querystring'\n\nconst mockAgent = new MockAgent()\nsetGlobalDispatcher(mockAgent)\n\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nconst matchPath = requestPath => {\n  const [pathname, search] = requestPath.split('?')\n  const requestQuery = querystring.parse(search)\n\n  if (!pathname.startsWith('/foo')) {\n    return false\n  }\n\n  if (!Object.keys(requestQuery).includes('foo') || requestQuery.foo !== 'bar') {\n    return false\n  }\n\n  return true\n}\n\nmockPool.intercept({\n  path: matchPath,\n  method: 'GET'\n}).reply(200, 'foo')\n\nconst result = await request('http://localhost:3000/foo?foo=bar')\n// Will match and return mocked data\n```\n\n### `MockPool.close()`\n\nCloses the mock pool and de-registers from associated MockAgent.\n\nReturns: `Promise<void>`\n\n#### Example - clean up after tests are complete\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent()\nconst mockPool = mockAgent.get('http://localhost:3000')\n\nawait mockPool.close()\n```\n\n### `MockPool.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `MockPool.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n#### Example - MockPool request\n\n```js\nimport { MockAgent } from 'undici'\n\nconst mockAgent = new MockAgent()\n\nconst mockPool = mockAgent.get('http://localhost:3000')\nmockPool.intercept({\n  path: '/foo',\n  method: 'GET',\n}).reply(200, 'foo')\n\nconst {\n  statusCode,\n  body\n} = await mockPool.request({\n  origin: 'http://localhost:3000',\n  path: '/foo',\n  method: 'GET'\n})\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n### `MockPool.cleanMocks()`\n\nThis method cleans up all the prepared mocks.\n\nReturns: `void`\n"
  },
  {
    "path": "docs/docs/api/Pool.md",
    "content": "# Class: Pool\n\nExtends: `undici.Dispatcher`\n\nA pool of [Client](/docs/docs/api/Client.md) instances connected to the same upstream target.\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## `new Pool(url[, options])`\n\nArguments:\n\n* **url** `URL | string` - It should only include the **protocol, hostname, and port**.\n* **options** `PoolOptions` (optional)\n\n### Parameter: `PoolOptions`\n\nExtends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)\n\n* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`\n* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances.\n* **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `Pool` and closed.   When set to `null`, `Client` instances will not be removed or closed based on age.\n\n## Instance Properties\n\n### `Pool.closed`\n\nImplements [Client.closed](/docs/docs/api/Client.md#clientclosed)\n\n### `Pool.destroyed`\n\nImplements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)\n\n### `Pool.stats`\n\nReturns [`PoolStats`](PoolStats.md) instance for this pool.\n\n## Instance Methods\n\n### `Pool.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `Pool.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `Pool.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `Pool.dispatch(options, handler)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `Pool.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `Pool.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `Pool.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `Pool.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n## Instance Events\n\n### Event: `'connect'`\n\nSee [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).\n\n### Event: `'disconnect'`\n\nSee [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).\n\n### Event: `'drain'`\n\nSee [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).\n"
  },
  {
    "path": "docs/docs/api/PoolStats.md",
    "content": "# Class: PoolStats\n\nAggregate stats for a [Pool](/docs/docs/api/Pool.md) or [BalancedPool](/docs/docs/api/BalancedPool.md).\n\n## `new PoolStats(pool)`\n\nArguments:\n\n* **pool** `Pool` - Pool or BalancedPool from which to return stats.\n\n## Instance Properties\n\n### `PoolStats.connected`\n\nNumber of open socket connections in this pool.\n\n### `PoolStats.free`\n\nNumber of open socket connections in this pool that do not have an active request.\n\n### `PoolStats.pending`\n\nNumber of pending requests across all clients in this pool.\n\n### `PoolStats.queued`\n\nNumber of queued requests across all clients in this pool.\n\n### `PoolStats.running`\n\nNumber of currently active requests across all clients in this pool.\n\n### `PoolStats.size`\n\nNumber of active, pending, or queued requests across all clients in this pool.\n"
  },
  {
    "path": "docs/docs/api/ProxyAgent.md",
    "content": "# Class: ProxyAgent\n\nExtends: `undici.Dispatcher`\n\nA Proxy Agent class that implements the Agent API. It allows the connection through proxy in a simple way.\n\n## `new ProxyAgent([options])`\n\nArguments:\n\n* **options** `ProxyAgentOptions` (required) - It extends the `Agent` options.\n\nReturns: `ProxyAgent`\n\n### Parameter: `ProxyAgentOptions`\n\nExtends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)\n> It ommits `AgentOptions#connect`.\n\n> **Note:** When `AgentOptions#connections` is set, and different from `0`, the non-standard [`proxy-connection` header](https://udger.com/resources/http-request-headers-detail?header=Proxy-Connection) will be set to `keep-alive` in the request.\n\n* **uri** `string | URL` (required) - The URI of the proxy server.  This can be provided as a string, as an instance of the URL class, or as an object with a `uri` property of type string.\nIf the `uri` is provided as a string or `uri` is an object with an `uri` property of type string, then it will be parsed into a `URL` object according to the [WHATWG URL Specification](https://url.spec.whatwg.org).\nFor detailed information on the parsing process and potential validation errors, please refer to the [\"Writing\" section](https://url.spec.whatwg.org/#writing) of the WHATWG URL Specification.\n* **token** `string` (optional) - It can be passed by a string of token for authentication.\n* **auth** `string` (**deprecated**) - Use token.\n* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`\n* **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).\n* **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).\n* **proxyTunnel** `boolean` (optional) - For connections involving secure protocols, Undici will always establish a tunnel via the HTTP2  CONNECT extension. If proxyTunnel is set to true, this will occur for unsecured proxy/endpoint connections as well. Currently, there is no way to facilitate HTTP1 IP tunneling as described in https://www.rfc-editor.org/rfc/rfc9484.html#name-http-11-request. If proxyTunnel is set to false (the default), ProxyAgent connections where both the Proxy and Endpoint are unsecured will issue all requests to the Proxy, and prefix the endpoint request path with the endpoint origin address.\n\nExamples:\n\n```js\nimport { ProxyAgent } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\n// or\nconst proxyAgent = new ProxyAgent(new URL('my.proxy.server'))\n// or\nconst proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })\n// or\nconst proxyAgent = new ProxyAgent({\n  uri: new URL('my.proxy.server'),\n  proxyTls: {\n    signal: AbortSignal.timeout(1000)\n  }\n})\n```\n\n#### Example - Basic ProxyAgent instantiation\n\nThis will instantiate the ProxyAgent. It will not do anything until registered as the agent to use with requests.\n\n```js\nimport { ProxyAgent } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\n```\n\n#### Example - Basic Proxy Request with global agent dispatcher\n\n```js\nimport { setGlobalDispatcher, request, ProxyAgent } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\nsetGlobalDispatcher(proxyAgent)\n\nconst { statusCode, body } = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Proxy Request with local agent dispatcher\n\n```js\nimport { ProxyAgent, request } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: proxyAgent })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic Proxy Request with authentication\n\n```js\nimport { setGlobalDispatcher, request, ProxyAgent } from 'undici';\n\nconst proxyAgent = new ProxyAgent({\n  uri: 'my.proxy.server',\n  // token: 'Bearer xxxx'\n  token: `Basic ${Buffer.from('username:password').toString('base64')}`\n});\nsetGlobalDispatcher(proxyAgent);\n\nconst { statusCode, body } = await request('http://localhost:3000/foo');\n\nconsole.log('response received', statusCode); // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')); // data foo\n}\n```\n\n### `ProxyAgent.close()`\n\nCloses the proxy agent and waits for registered pools and clients to also close before resolving.\n\nReturns: `Promise<void>`\n\n#### Example - clean up after tests are complete\n\n```js\nimport { ProxyAgent, setGlobalDispatcher } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\nsetGlobalDispatcher(proxyAgent)\n\nawait proxyAgent.close()\n```\n\n### `ProxyAgent.dispatch(options, handlers)`\n\nImplements [`Agent.dispatch(options, handlers)`](/docs/docs/api/Agent.md#parameter-agentdispatchoptions).\n\n### `ProxyAgent.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n\n#### Example - ProxyAgent with Fetch\n\nThis example demonstrates how to use `fetch` with a proxy via `ProxyAgent`. It is particularly useful for scenarios requiring proxy tunneling.\n\n```javascript\nimport { ProxyAgent, fetch } from 'undici';\n\n// Define the ProxyAgent\nconst proxyAgent = new ProxyAgent('http://localhost:8000');\n\n// Make a GET request through the proxy\nconst response = await fetch('http://localhost:3000/foo', {\n  dispatcher: proxyAgent,\n  method: 'GET',\n});\n\nconsole.log('Response status:', response.status);\nconsole.log('Response data:', await response.text());\n```\n\n---\n\n#### Example - ProxyAgent with a Custom Proxy Server\n\nThis example shows how to create a custom proxy server and use it with `ProxyAgent`.\n\n```javascript\nimport * as http from 'node:http';\nimport { createProxy } from 'proxy';\nimport { ProxyAgent, fetch } from 'undici';\n\n// Create a proxy server\nconst proxyServer = createProxy(http.createServer());\nproxyServer.listen(8000, () => {\n  console.log('Proxy server running on port 8000');\n});\n\n// Define and use the ProxyAgent\nconst proxyAgent = new ProxyAgent('http://localhost:8000');\n\nconst response = await fetch('http://example.com', {\n  dispatcher: proxyAgent,\n  method: 'GET',\n});\n\nconsole.log('Response status:', response.status);\nconsole.log('Response data:', await response.text());\n```\n\n---\n\n#### Example - ProxyAgent with HTTPS Tunneling\n\nThis example demonstrates how to perform HTTPS tunneling using a proxy.\n\n```javascript\nimport { ProxyAgent, fetch } from 'undici';\n\n// Define a ProxyAgent for HTTPS proxy\nconst proxyAgent = new ProxyAgent('https://secure.proxy.server');\n\n// Make a request to an HTTPS endpoint via the proxy\nconst response = await fetch('https://secure.endpoint.com/api/data', {\n  dispatcher: proxyAgent,\n  method: 'GET',\n});\n\nconsole.log('Response status:', response.status);\nconsole.log('Response data:', await response.json());\n```\n\n#### Example - ProxyAgent as a Global Dispatcher\n\n`ProxyAgent` can be configured as a global dispatcher, making it available for all requests without explicitly passing it. This simplifies code and is useful when a single proxy configuration applies to all requests.\n\n```javascript\nimport { ProxyAgent, setGlobalDispatcher, fetch } from 'undici';\n\n// Define and configure the ProxyAgent\nconst proxyAgent = new ProxyAgent('http://localhost:8000');\nsetGlobalDispatcher(proxyAgent);\n\n// Make requests without specifying the dispatcher\nconst response = await fetch('http://example.com');\nconsole.log('Response status:', response.status);\nconsole.log('Response data:', await response.text());\n"
  },
  {
    "path": "docs/docs/api/RedirectHandler.md",
    "content": "# Class: RedirectHandler\n\nA class that handles redirection logic for HTTP requests.\n\n## `new RedirectHandler(dispatch, maxRedirections, opts, handler, redirectionLimitReached)`\n\nArguments:\n\n- **dispatch** `function` - The dispatch function to be called after every retry.\n- **maxRedirections** `number` - Maximum number of redirections allowed.\n- **opts** `object` - Options for handling redirection.\n- **handler** `object` - An object containing handlers for different stages of the request lifecycle.\n- **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.\n\nReturns: `RedirectHandler`\n\n### Parameters\n\n- **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandler) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every redirection.\n- **maxRedirections** `number` (required) - Maximum number of redirections allowed.\n- **opts** `object` (required) - Options for handling redirection.\n- **handler** `object` (required) - Handlers for different stages of the request lifecycle.\n- **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.\n\n### Properties\n\n- **location** `string` - The current redirection location.\n- **abort** `function` - The abort function.\n- **opts** `object` - The options for handling redirection.\n- **maxRedirections** `number` - Maximum number of redirections allowed.\n- **handler** `object` - Handlers for different stages of the request lifecycle.\n- **history** `Array` - An array representing the history of URLs during redirection.\n- **redirectionLimitReached** `boolean` - Indicates whether the redirection limit has been reached.\n\n### Methods\n\n#### `onConnect(abort)`\n\nCalled when the connection is established.\n\nParameters:\n\n- **abort** `function` - The abort function.\n\n#### `onUpgrade(statusCode, headers, socket)`\n\nCalled when an upgrade is requested.\n\nParameters:\n\n- **statusCode** `number` - The HTTP status code.\n- **headers** `object` - The headers received in the response.\n- **socket** `object` - The socket object.\n\n#### `onError(error)`\n\nCalled when an error occurs.\n\nParameters:\n\n- **error** `Error` - The error that occurred.\n\n#### `onHeaders(statusCode, headers, resume, statusText)`\n\nCalled when headers are received.\n\nParameters:\n\n- **statusCode** `number` - The HTTP status code.\n- **headers** `object` - The headers received in the response.\n- **resume** `function` - The resume function.\n- **statusText** `string` - The status text.\n\n#### `onData(chunk)`\n\nCalled when data is received.\n\nParameters:\n\n- **chunk** `Buffer` - The data chunk received.\n\n#### `onComplete(trailers)`\n\nCalled when the request is complete.\n\nParameters:\n\n- **trailers** `object` - The trailers received.\n\n#### `onBodySent(chunk)`\n\nCalled when the request body is sent.\n\nParameters:\n\n- **chunk** `Buffer` - The chunk of the request body sent.\n"
  },
  {
    "path": "docs/docs/api/RetryAgent.md",
    "content": "# Class: RetryAgent\n\nExtends: `undici.Dispatcher`\n\nA `undici.Dispatcher` that allows to automatically retry a request.\nWraps a `undici.RetryHandler`.\n\n## `new RetryAgent(dispatcher, [options])`\n\nArguments:\n\n* **dispatcher** `undici.Dispatcher` (required) - the dispatcher to wrap\n* **options** `RetryHandlerOptions` (optional) - the options\n\nReturns: `ProxyAgent`\n\n### Parameter: `RetryHandlerOptions`\n\n- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler. Default: `true`\n- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.\n- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`\n- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)\n- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)\n- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`\n- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`\n-\n- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`\n- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`\n- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`\n\n**`RetryContext`**\n\n- `state`: `RetryState` - Current retry state. It can be mutated.\n- `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.\n\nExample:\n\n```js\nimport { Agent, RetryAgent } from 'undici'\n\nconst agent = new RetryAgent(new Agent())\n\nconst res = await agent.request({\n  method: 'GET',\n  origin: 'http://example.com',\n  path: '/',\n})\nconsole.log(res.statusCode)\nconsole.log(await res.body.text())\n```\n"
  },
  {
    "path": "docs/docs/api/RetryHandler.md",
    "content": "# Class: RetryHandler\n\nExtends: `undici.DispatcherHandlers`\n\nA handler class that implements the retry logic for a request.\n\n## `new RetryHandler(dispatchOptions, retryHandlers, [retryOptions])`\n\nArguments:\n\n- **options** `Dispatch.DispatchOptions & RetryOptions` (required) - It is an intersection of `Dispatcher.DispatchOptions` and `RetryOptions`.\n- **retryHandlers** `RetryHandlers` (required) - Object containing the `dispatch` to be used on every retry, and `handler` for handling the `dispatch` lifecycle.\n\nReturns: `retryHandler`\n\n### Parameter: `Dispatch.DispatchOptions & RetryOptions`\n\nExtends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions).\n\n#### `RetryOptions`\n\n- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler.\n- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => number | null` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.\n- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`\n- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)\n- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)\n- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`\n- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`\n-\n- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`\n- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`\n- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`\n\n**`RetryContext`**\n\n- `state`: `RetryState` - Current retry state. It can be mutated.\n- `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.\n\n**`RetryState`**\n\nIt represents the retry state for a given request.\n\n- `counter`: `number` - Current retry attempt.\n\n### Parameter `RetryHandlers`\n\n- **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandler) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every retry.\n- **handler** Extends [`Dispatch.DispatchHandler`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler) (required) - Handler function to be called after the request is successful or the retries are exhausted.\n\n>__Note__: The `RetryHandler` does not retry over stateful bodies (e.g. streams, AsyncIterable) as those, once consumed, are left in a state that cannot be reutilized. For these situations the `RetryHandler` will identify\n>the body as stateful and will not retry the request rejecting with the error `UND_ERR_REQ_RETRY`.\n\nExamples:\n\n```js\nconst client = new Client(`http://localhost:${server.address().port}`);\nconst chunks = [];\nconst handler = new RetryHandler(\n  {\n    ...dispatchOptions,\n    retryOptions: {\n      // custom retry function\n      retry: function (err, state, callback) {\n        counter++;\n\n        if (err.code && err.code === \"UND_ERR_DESTROYED\") {\n          callback(err);\n          return;\n        }\n\n        if (err.statusCode === 206) {\n          callback(err);\n          return;\n        }\n\n        setTimeout(() => callback(null), 1000);\n      },\n    },\n  },\n  {\n    dispatch: (...args) => {\n      return client.dispatch(...args);\n    },\n    handler: {\n      onConnect() {},\n      onBodySent() {},\n      onHeaders(status, _rawHeaders, resume, _statusMessage) {\n        // do something with headers\n      },\n      onData(chunk) {\n        chunks.push(chunk);\n        return true;\n      },\n      onComplete() {},\n      onError() {\n        // handle error properly\n      },\n    },\n  }\n);\n```\n\n#### Example - Basic RetryHandler with defaults\n\n```js\nconst client = new Client(`http://localhost:${server.address().port}`);\nconst handler = new RetryHandler(dispatchOptions, {\n  dispatch: client.dispatch.bind(client),\n  handler: {\n    onConnect() {},\n    onBodySent() {},\n    onHeaders(status, _rawHeaders, resume, _statusMessage) {},\n    onData(chunk) {},\n    onComplete() {},\n    onError(err) {},\n  },\n});\n```\n"
  },
  {
    "path": "docs/docs/api/RoundRobinPool.md",
    "content": "# Class: RoundRobinPool\n\nExtends: `undici.Dispatcher`\n\nA pool of [Client](/docs/docs/api/Client.md) instances connected to the same upstream target with round-robin client selection.\n\nUnlike [`Pool`](/docs/docs/api/Pool.md), which always selects the first available client, `RoundRobinPool` cycles through clients in a round-robin fashion. This ensures even distribution of requests across all connections, which is particularly useful when the upstream target is behind a load balancer that round-robins TCP connections across multiple backend servers (e.g., Kubernetes Services).\n\nRequests are not guaranteed to be dispatched in order of invocation.\n\n## `new RoundRobinPool(url[, options])`\n\nArguments:\n\n* **url** `URL | string` - It should only include the **protocol, hostname, and port**.\n* **options** `RoundRobinPoolOptions` (optional)\n\n### Parameter: `RoundRobinPoolOptions`\n\nExtends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)\n\n* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`\n* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `RoundRobinPool` instance will create an unlimited amount of `Client` instances.\n* **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `RoundRobinPool` and closed. When set to `null`, `Client` instances will not be removed or closed based on age.\n\n## Use Case\n\n`RoundRobinPool` is designed for scenarios where:\n\n1. You connect to a single origin (e.g., `http://my-service.namespace.svc`)\n2. That origin is backed by a load balancer distributing TCP connections across multiple servers\n3. You want requests evenly distributed across all backend servers\n\n**Example**: In Kubernetes, when using a Service DNS name with multiple Pod replicas, kube-proxy load balances TCP connections. `RoundRobinPool` ensures each connection (and thus each Pod) receives an equal share of requests.\n\n### Important: Backend Distribution Considerations\n\n`RoundRobinPool` distributes **HTTP requests** evenly across **TCP connections**. Whether this translates to even backend server distribution depends on the load balancer's behavior:\n\n**✓ Works when the load balancer**:\n- Assigns different backends to different TCP connections from the same client\n- Uses algorithms like: round-robin, random, least-connections (without client affinity)\n- Example: Default Kubernetes Services without `sessionAffinity`\n\n**✗ Does NOT work when**:\n- Load balancer has client/source IP affinity (all connections from one IP → same backend)\n- Load balancer uses source-IP-hash or sticky sessions\n\n**How it works:**\n1. `RoundRobinPool` creates N TCP connections to the load balancer endpoint\n2. Load balancer assigns each TCP connection to a backend (per its algorithm)\n3. `RoundRobinPool` cycles HTTP requests across those N connections\n4. Result: Requests distributed proportionally to how the LB distributed the connections\n\nIf the load balancer assigns all connections to the same backend (e.g., due to session affinity), `RoundRobinPool` cannot overcome this. In such cases, consider using [`BalancedPool`](/docs/docs/api/BalancedPool.md) with direct backend addresses (e.g., individual pod IPs) instead of a load-balanced endpoint.\n\n## Instance Properties\n\n### `RoundRobinPool.closed`\n\nImplements [Client.closed](/docs/docs/api/Client.md#clientclosed)\n\n### `RoundRobinPool.destroyed`\n\nImplements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)\n\n### `RoundRobinPool.stats`\n\nReturns [`PoolStats`](PoolStats.md) instance for this pool.\n\n## Instance Methods\n\n### `RoundRobinPool.close([callback])`\n\nImplements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).\n\n### `RoundRobinPool.destroy([error, callback])`\n\nImplements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).\n\n### `RoundRobinPool.connect(options[, callback])`\n\nSee [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).\n\n### `RoundRobinPool.dispatch(options, handler)`\n\nImplements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).\n\n### `RoundRobinPool.pipeline(options, handler)`\n\nSee [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).\n\n### `RoundRobinPool.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n### `RoundRobinPool.stream(options, factory[, callback])`\n\nSee [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).\n\n### `RoundRobinPool.upgrade(options[, callback])`\n\nSee [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).\n\n## Instance Events\n\n### Event: `'connect'`\n\nSee [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).\n\n### Event: `'disconnect'`\n\nSee [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).\n\n### Event: `'drain'`\n\nSee [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).\n\n## Example\n\n```javascript\nimport { RoundRobinPool } from 'undici'\n\nconst pool = new RoundRobinPool('http://my-service.default.svc.cluster.local', {\n  connections: 10\n})\n\n// Requests will be distributed evenly across all 10 connections\nfor (let i = 0; i < 100; i++) {\n  const { body } = await pool.request({\n    path: '/api/data',\n    method: 'GET'\n  })\n  console.log(await body.json())\n}\n\nawait pool.close()\n```\n\n## See Also\n\n- [Pool](/docs/docs/api/Pool.md) - Connection pool without round-robin\n- [BalancedPool](/docs/docs/api/BalancedPool.md) - Load balancing across multiple origins\n- [Issue #3648](https://github.com/nodejs/undici/issues/3648) - Original issue describing uneven distribution\n\n"
  },
  {
    "path": "docs/docs/api/SnapshotAgent.md",
    "content": "# SnapshotAgent\n\nThe `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.\n\n## Use Cases\n\n- **Integration Testing**: Record real API interactions and replay them in tests\n- **Offline Development**: Work with APIs without network connectivity\n- **Consistent Test Data**: Ensure tests use the same responses across runs\n- **API Contract Testing**: Capture and validate API behavior over time\n\n## Constructor\n\n```javascript\nnew SnapshotAgent([options])\n```\n\n### Parameters\n\n- **options** `Object` (optional)\n  - **mode** `String` - The snapshot mode: `'record'`, `'playback'`, or `'update'`. Default: `'record'`\n  - **snapshotPath** `String` - Path to the snapshot file for loading/saving\n  - **maxSnapshots** `Number` - Maximum number of snapshots to keep in memory. Default: `Infinity`\n  - **autoFlush** `Boolean` - Whether to automatically save snapshots to disk. Default: `false`\n  - **flushInterval** `Number` - Interval in milliseconds for auto-flush. Default: `30000`\n  - **matchHeaders** `Array<String>` - Specific headers to include in request matching. Default: all headers\n  - **ignoreHeaders** `Array<String>` - Headers to ignore during request matching\n  - **excludeHeaders** `Array<String>` - Headers to exclude from snapshots (for security)\n  - **matchBody** `Boolean` - Whether to include request body in matching. Default: `true`\n  - **matchQuery** `Boolean` - Whether to include query parameters in matching. Default: `true`\n  - **caseSensitive** `Boolean` - Whether header matching is case-sensitive. Default: `false`\n  - **shouldRecord** `Function` - Callback to determine if a request should be recorded\n  - **shouldPlayback** `Function` - Callback to determine if a request should be played back\n  - **excludeUrls** `Array` - URL patterns (strings or RegExp) to exclude from recording/playback\n  - All other options from `MockAgent` are supported\n\n### Modes\n\n#### Record Mode (`'record'`)\nMakes real HTTP requests and saves the responses to snapshots.\n\n```javascript\nimport { 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```\n\n#### Playback Mode (`'playback'`)\nReplays recorded responses without making real HTTP requests.\n\n```javascript\nimport { 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```\n\n#### Update Mode (`'update'`)\nUses existing snapshots when available, but records new ones for missing requests.\n\n```javascript\nimport { 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```\n\n## Instance Methods\n\n### `agent.saveSnapshots([filePath])`\n\nSaves all recorded snapshots to a file.\n\n#### Parameters\n\n- **filePath** `String` (optional) - Path to save snapshots. Uses constructor `snapshotPath` if not provided.\n\n#### Returns\n\n`Promise<void>`\n\n```javascript\nawait agent.saveSnapshots('./custom-snapshots.json')\n```\n\n## Advanced Configuration\n\n### Header Filtering\n\nControl which headers are used for request matching and what gets stored in snapshots:\n\n```javascript\nconst 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```\n\n### Custom Request/Response Filtering\n\nUse callback functions to determine what gets recorded or played back:\n\n```javascript\nconst 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' && 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```\n\n### URL Pattern Exclusion\n\nExclude specific URLs from recording/playback using patterns:\n\n```javascript\nconst 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```\n\n### Memory Management\n\nConfigure automatic memory and disk management:\n\n```javascript\nconst 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```\n\n### Sequential Response Handling\n\nHandle multiple responses for the same request (similar to nock):\n\n```javascript\n// 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```\n\n## Managing Snapshots\n\n### Replacing Existing Snapshots\n\n```javascript\n// 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```\n\n### `agent.loadSnapshots([filePath])`\n\nLoads snapshots from a file.\n\n#### Parameters\n\n- **filePath** `String` (optional) - Path to load snapshots from. Uses constructor `snapshotPath` if not provided.\n\n#### Returns\n\n`Promise<void>`\n\n```javascript\nawait agent.loadSnapshots('./existing-snapshots.json')\n```\n\n### `agent.getRecorder()`\n\nGets the underlying `SnapshotRecorder` instance.\n\n#### Returns\n\n`SnapshotRecorder`\n\n```javascript\nconst recorder = agent.getRecorder()\nconsole.log(`Recorded ${recorder.size()} interactions`)\n```\n\n### `agent.getMode()`\n\nGets the current snapshot mode.\n\n#### Returns\n\n`String` - The current mode (`'record'`, `'playback'`, or `'update'`)\n\n### `agent.clearSnapshots()`\n\nClears all recorded snapshots from memory.\n\n```javascript\nagent.clearSnapshots()\n```\n\n## Working with Different Request Types\n\n### GET Requests\n\n```javascript\n// 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```\n\n### POST Requests with Body\n\n```javascript\n// 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```\n\n### Using with `undici.request`\n\nSnapshotAgent works with all undici APIs, not just fetch:\n\n```javascript\nimport { 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```\n\n## Test Integration\n\n### Basic Test Setup\n\n```javascript\nimport { 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```\n\n### Environment-Based Mode Selection\n\n```javascript\nconst 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```\n\n### Test Helper Function\n\n```javascript\nfunction 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```\n\n## Snapshot File Format\n\nSnapshots are stored as JSON with the following structure:\n\n```json\n[\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```\n\n## Security Considerations\n\n### Sensitive Data in Snapshots\n\nBy default, SnapshotAgent records all headers and request/response data. For production use, always exclude sensitive information:\n\n```javascript\nconst 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 && 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```\n\n### Snapshot File Security\n\n**Important**: Snapshot files may contain sensitive data. Handle them securely:\n\n- ✅ Add snapshot files to `.gitignore` if they contain real API data\n- ✅ Use environment-specific snapshots (dev/staging/prod)\n- ✅ Regularly review snapshot contents for sensitive information\n- ✅ Use the `excludeHeaders` option for production snapshots\n- ❌ Never commit snapshots with real authentication tokens\n- ❌ Don't share snapshot files containing personal data\n\n```gitignore\n# 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```\n\n## Error Handling\n\n### Missing Snapshots in Playback Mode\n\n```javascript\ntry {\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```\n\n### Handling Network Errors in Record Mode\n\n```javascript\nconst 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```\n\n## Best Practices\n\n### 1. Organize Snapshots by Test Suite\n\n```javascript\n// Use descriptive snapshot file names\nconst agent = new SnapshotAgent({\n  mode: 'playback',\n  snapshotPath: `./test/snapshots/${testSuiteName}-${testName}.json`\n})\n```\n\n### 2. Version Control Snapshots\n\nAdd snapshot files to version control to ensure consistent test behavior across environments:\n\n```gitignore\n# Include snapshots in version control\n!/test/snapshots/*.json\n```\n\n### 3. Clean Up Test Data\n\n```javascript\ntest('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```\n\n### 4. Snapshot Validation\n\n```javascript\ntest('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```\n\n## Comparison with Other Tools\n\n### vs Manual MockAgent Setup\n\n**Manual MockAgent:**\n```javascript\nconst 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```\n\n**SnapshotAgent:**\n```javascript\n// 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```\n\n### vs nock\n\nSnapshotAgent provides similar functionality to nock but is specifically designed for undici:\n\n- ✅ Works with all undici APIs (`request`, `stream`, `pipeline`, etc.)\n- ✅ Supports undici-specific features (RetryAgent, connection pooling)\n- ✅ Better TypeScript integration\n- ✅ More efficient for high-performance scenarios\n\n## See Also\n\n- [MockAgent](./MockAgent.md) - Manual mocking for more control\n- [MockCallHistory](./MockCallHistory.md) - Inspecting request history\n- [Testing Best Practices](../best-practices/writing-tests.md) - General testing guidance"
  },
  {
    "path": "docs/docs/api/Socks5ProxyAgent.md",
    "content": "# Class: Socks5ProxyAgent\n\nExtends: `undici.Dispatcher`\n\nA SOCKS5 proxy wrapper class that implements the Dispatcher API. It enables HTTP requests to be routed through a SOCKS5 proxy server, providing connection tunneling and authentication support.\n\n## `new Socks5ProxyAgent(proxyUrl[, options])`\n\nArguments:\n\n* **proxyUrl** `string | URL` (required) - The SOCKS5 proxy server URL. Must use `socks5://` or `socks://` protocol.\n* **options** `Socks5ProxyAgent.Options` (optional) - Additional configuration options.\n\nReturns: `Socks5ProxyAgent`\n\n### Parameter: `Socks5ProxyAgent.Options`\n\nExtends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)\n\n* **headers** `IncomingHttpHeaders` (optional) - Additional headers to send with proxy connections.\n* **username** `string` (optional) - SOCKS5 proxy username for authentication. Can also be provided in the proxy URL.\n* **password** `string` (optional) - SOCKS5 proxy password for authentication. Can also be provided in the proxy URL.\n* **connect** `Function` (optional) - Custom connector function for the proxy connection.\n* **proxyTls** `BuildOptions` (optional) - TLS options for the proxy connection (when using SOCKS5 over TLS).\n\nExamples:\n\n```js\nimport { Socks5ProxyAgent } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n// or with authentication\nconst socks5ProxyWithAuth = new Socks5ProxyAgent('socks5://user:pass@localhost:1080')\n// or with options\nconst socks5ProxyWithOptions = new Socks5ProxyAgent('socks5://localhost:1080', {\n  username: 'user',\n  password: 'pass',\n  connections: 10\n})\n```\n\n#### Example - Basic SOCKS5 Proxy instantiation\n\nThis will instantiate the Socks5ProxyAgent. It will not do anything until registered as the dispatcher to use with requests.\n\n```js\nimport { Socks5ProxyAgent } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n```\n\n#### Example - Basic SOCKS5 Proxy Request with global dispatcher\n\n```js\nimport { setGlobalDispatcher, request, Socks5ProxyAgent } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\nsetGlobalDispatcher(socks5Proxy)\n\nconst { statusCode, body } = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - Basic SOCKS5 Proxy Request with local dispatcher\n\n```js\nimport { Socks5ProxyAgent, request } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n\nconst {\n  statusCode,\n  body\n} = await request('http://localhost:3000/foo', { dispatcher: socks5Proxy })\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - SOCKS5 Proxy Request with authentication\n\n```js\nimport { setGlobalDispatcher, request, Socks5ProxyAgent } from 'undici'\n\n// Authentication via URL\nconst socks5Proxy = new Socks5ProxyAgent('socks5://username:password@localhost:1080')\n\n// Or authentication via options\n// const socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080', {\n//   username: 'username',\n//   password: 'password'\n// })\n\nsetGlobalDispatcher(socks5Proxy)\n\nconst { statusCode, body } = await request('http://localhost:3000/foo')\n\nconsole.log('response received', statusCode) // response received 200\n\nfor await (const data of body) {\n  console.log('data', data.toString('utf8')) // data foo\n}\n```\n\n#### Example - SOCKS5 Proxy with HTTPS requests\n\nSOCKS5 proxy supports both HTTP and HTTPS requests through tunneling:\n\n```js\nimport { Socks5ProxyAgent, request } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n\nconst response = await request('https://api.example.com/data', {\n  dispatcher: socks5Proxy,\n  method: 'GET'\n})\n\nconsole.log('Response status:', response.statusCode)\nconsole.log('Response data:', await response.body.json())\n```\n\n#### Example - SOCKS5 Proxy with Fetch\n\n```js\nimport { Socks5ProxyAgent, fetch } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n\nconst response = await fetch('http://localhost:3000/api/users', {\n  dispatcher: socks5Proxy,\n  method: 'GET'\n})\n\nconsole.log('Response status:', response.status)\nconsole.log('Response data:', await response.text())\n```\n\n#### Example - Connection Pooling\n\nSOCKS5ProxyWrapper automatically manages connection pooling for better performance:\n\n```js\nimport { Socks5ProxyAgent, request } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080', {\n  connections: 10, // Allow up to 10 concurrent connections\n  pipelining: 1    // Enable HTTP/1.1 pipelining\n})\n\n// Multiple requests will reuse connections through the SOCKS5 tunnel\nconst responses = await Promise.all([\n  request('http://api.example.com/endpoint1', { dispatcher: socks5Proxy }),\n  request('http://api.example.com/endpoint2', { dispatcher: socks5Proxy }),\n  request('http://api.example.com/endpoint3', { dispatcher: socks5Proxy })\n])\n\nconsole.log('All requests completed through the same SOCKS5 proxy')\n```\n\n### `Socks5ProxyAgent.close()`\n\nCloses the SOCKS5 proxy wrapper and waits for all underlying pools and connections to close before resolving.\n\nReturns: `Promise<void>`\n\n#### Example - clean up after tests are complete\n\n```js\nimport { Socks5ProxyAgent, setGlobalDispatcher } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\nsetGlobalDispatcher(socks5Proxy)\n\n// ... make requests\n\nawait socks5Proxy.close()\n```\n\n### `Socks5ProxyAgent.destroy([err])`\n\nDestroys the SOCKS5 proxy wrapper and all underlying connections immediately.\n\nArguments:\n* **err** `Error` (optional) - The error that caused the destruction.\n\nReturns: `Promise<void>`\n\n#### Example - force close all connections\n\n```js\nimport { Socks5ProxyAgent } from 'undici'\n\nconst socks5Proxy = new Socks5ProxyAgent('socks5://localhost:1080')\n\n// Force close all connections\nawait socks5Proxy.destroy()\n```\n\n### `Socks5ProxyAgent.dispatch(options, handlers)`\n\nImplements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handlers).\n\n### `Socks5ProxyAgent.request(options[, callback])`\n\nSee [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).\n\n## Debugging\n\nSOCKS5 proxy connections can be debugged using Node.js diagnostics:\n\n```sh\nNODE_DEBUG=undici:socks5 node script.js\n```\n\nThis will output detailed information about the SOCKS5 handshake, authentication, and connection establishment.\n\n## SOCKS5 Protocol Support\n\nThe Socks5ProxyAgent supports the following SOCKS5 features:\n\n### Authentication Methods\n\n- **No Authentication** (`0x00`) - For public or internal proxies\n- **Username/Password** (`0x02`) - RFC 1929 authentication\n\n### Address Types\n\n- **IPv4** (`0x01`) - Standard IPv4 addresses\n- **Domain Name** (`0x03`) - Domain names (recommended for flexibility)\n- **IPv6** (`0x04`) - IPv6 addresses (full support for standard and compressed notation)\n\n### Commands\n\n- **CONNECT** (`0x01`) - Establish TCP connection (primary use case for HTTP)\n\n### Error Handling\n\nThe wrapper handles various SOCKS5 error conditions:\n\n- Connection refused by proxy\n- Authentication failures\n- Network unreachable\n- Host unreachable\n- Unsupported address types or commands\n\n## Performance Considerations\n\n- **Connection Pooling**: Automatically pools connections through the SOCKS5 tunnel for better performance\n- **HTTP/1.1 Pipelining**: Supports pipelining when enabled\n- **DNS Resolution**: Domain names are resolved by the SOCKS5 proxy, reducing local DNS queries\n- **TLS Termination**: HTTPS connections are encrypted end-to-end, with the SOCKS5 proxy only handling the TCP tunnel\n\n## Security Notes\n\n1. **Authentication**: Credentials are sent to the SOCKS5 proxy in plaintext unless using SOCKS5 over TLS\n2. **DNS Leaks**: All DNS resolution happens on the proxy server, preventing DNS leaks\n3. **End-to-end Encryption**: HTTPS traffic remains encrypted between client and final destination\n4. **Connection Security**: Consider using authenticated proxies and secure networks\n\n## Compatibility\n\n- **Protocol**: SOCKS5 (RFC 1928) with Username/Password Authentication (RFC 1929)\n- **Transport**: TCP only (UDP support not implemented)\n- **Node.js**: Compatible with all supported Node.js versions\n- **HTTP Versions**: Works with HTTP/1.1 and HTTP/2 over the tunnel"
  },
  {
    "path": "docs/docs/api/Util.md",
    "content": "# Util\n\nUtility API for third-party implementations of the dispatcher API.\n\n## `parseHeaders(headers, [obj])`\n\nReceives a header object and returns the parsed value.\n\nArguments:\n\n- **headers** `(Buffer | string | (Buffer | string)[])[]` (required) - Header object.\n\n- **obj** `Record<string, string | string[]>` (optional) - Object to specify a proxy object. The parsed value is assigned to this object. But, if **headers** is an object, it is not used.\n\nReturns: `Record<string, string | string[]>` If **obj** is specified, it is equivalent to **obj**.\n\n## `headerNameToString(value)`\n\nRetrieves a header name and returns its lowercase value.\n\nArguments:\n\n- **value** `string | Buffer` (required) - Header name.\n\nReturns: `string`\n"
  },
  {
    "path": "docs/docs/api/WebSocket.md",
    "content": "# Class: WebSocket\n\nExtends: [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)\n\nThe WebSocket object provides a way to manage a WebSocket connection to a server, allowing bidirectional communication. The API follows the [WebSocket spec](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455).\n\n## `new WebSocket(url[, protocol])`\n\nArguments:\n\n* **url** `URL | string`\n* **protocol** `string | string[] | WebSocketInit` (optional) - Subprotocol(s) to request the server use, or a [`Dispatcher`](/docs/docs/api/Dispatcher.md).\n\n### WebSocketInit\n\nWhen passing an object as the second argument, the following options are available:\n\n* **protocols** `string | string[]` (optional) - Subprotocol(s) to request the server use.\n* **dispatcher** `Dispatcher` (optional) - A custom [`Dispatcher`](/docs/docs/api/Dispatcher.md) to use for the connection.\n* **headers** `HeadersInit` (optional) - Custom headers to include in the WebSocket handshake request.\n\n### Example:\n\nThis example will not work in browsers or other platforms that don't allow passing an object.\n\n```js\nimport { WebSocket, ProxyAgent } from 'undici'\n\nconst proxyAgent = new ProxyAgent('my.proxy.server')\n\nconst ws = new WebSocket('wss://echo.websocket.events', {\n  dispatcher: proxyAgent,\n  protocols: ['echo', 'chat']\n})\n```\n\nIf you do not need a custom Dispatcher, it's recommended to use the following pattern:\n\n```js\nimport { WebSocket } from 'undici'\n\nconst ws = new WebSocket('wss://echo.websocket.events', ['echo', 'chat'])\n```\n\n### Example with HTTP/2:\n\n> ⚠️ Warning: WebSocket over HTTP/2 is experimental, it is likely to change in the future.\n\n> 🗒️ Note: WebSocket over HTTP/2 may be enabled by default in a future version,\n> this will happen by enabling HTTP/2 connections as the default behavior of Undici's Agent as well the global dispatcher.\n> Stay tuned to the changelog for more information.\n\nThis example will not work in browsers or other platforms that don't allow passing an object.\n\n```js\nimport { Agent } from 'undici'\n\nconst agent = new Agent({ allowH2: true })\n\nconst ws = new WebSocket('wss://echo.websocket.events', {\n  dispatcher: agent,\n  protocols: ['echo', 'chat']\n})\n```\n\n# Class: WebSocketStream\n\n> ⚠️ Warning: the WebSocketStream API has not been finalized and is likely to change.\n\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebSocketStream) for more information.\n\n## `new WebSocketStream(url[, protocol])`\n\nArguments:\n\n* **url** `URL | string`\n* **options** `WebSocketStreamOptions` (optional)\n\n### WebSocketStream Example\n\n```js\nconst stream = new WebSocketStream('https://echo.websocket.org/')\nconst { readable, writable } = await stream.opened\n\nasync function read () {\n  /** @type {ReadableStreamReader} */\n  const reader = readable.getReader()\n\n  while (true) {\n    const { done, value } = await reader.read()\n    if (done) break\n\n    // do something with value\n  }\n}\n\nasync function write () {\n  /** @type {WritableStreamDefaultWriter} */\n  const writer = writable.getWriter()\n  writer.write('Hello, world!')\n  writer.releaseLock()\n}\n\nread()\n\nsetInterval(() => write(), 5000)\n\n```\n\n## ping(websocket, payload)\nArguments:\n\n* **websocket** `WebSocket` - The WebSocket instance to send the ping frame on\n* **payload** `Buffer|undefined` (optional) - Optional payload data to include with the ping frame. Must not exceed 125 bytes.\n\nSends a ping frame to the WebSocket server. The server must respond with a pong frame containing the same payload data. This can be used for keepalive purposes or to verify that the connection is still active.\n\n### Example:\n\n```js\nimport { WebSocket, ping } from 'undici'\n\nconst ws = new WebSocket('wss://echo.websocket.events')\n\nws.addEventListener('open', () => {\n  // Send ping with no payload\n  ping(ws)\n\n  // Send ping with payload\n  const payload = Buffer.from('hello')\n  ping(ws, payload)\n})\n```\n\n**Note**: A ping frame cannot have a payload larger than 125 bytes. The ping will only be sent if the WebSocket connection is in the OPEN state.\n\n## Read More\n\n- [MDN - WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)\n- [The WebSocket Specification](https://www.rfc-editor.org/rfc/rfc6455)\n- [The WHATWG WebSocket Specification](https://websockets.spec.whatwg.org/)\n"
  },
  {
    "path": "docs/docs/api/api-lifecycle.md",
    "content": "# Client Lifecycle\n\nAn Undici [Client](/docs/docs/api/Client.md) can be best described as a state machine. The following list is a summary of the various state transitions the `Client` will go through in its lifecycle. This document also contains detailed breakdowns of each state.\n\n> This diagram is not a perfect representation of the undici Client. Since the Client class is not actually implemented as a state-machine, actual execution may deviate slightly from what is described below. Consider this as a general resource for understanding the inner workings of the Undici client rather than some kind of formal specification.\n\n## State Transition Overview\n\n* A `Client` begins in the **idle** state with no socket connection and no requests in queue.\n  * The *connect* event transitions the `Client` to the **pending** state where requests can be queued prior to processing.\n  * The *close* and *destroy* events transition the `Client` to the **destroyed** state. Since there are no requests in the queue, the *close* event immediately transitions to the **destroyed** state.\n* The **pending** state indicates the underlying socket connection has been successfully established and requests are queueing.\n  * The *process* event transitions the `Client` to the **processing** state where requests are processed.\n  * If requests are queued, the *close* event transitions to the **processing** state; otherwise, it transitions to the **destroyed** state.\n  * The *destroy* event transitions to the **destroyed** state.\n* The **processing** state initializes to the **processing.running** state.\n  * If the current request requires draining, the *needDrain* event transitions the `Client` into the **processing.busy** state which will return to the **processing.running** state with the *drainComplete* event.\n  * After all queued requests are completed, the *keepalive* event transitions the `Client` back to the **pending** state. If no requests are queued during the timeout, the **close** event transitions the `Client` to the **destroyed** state.\n  * If the *close* event is fired while the `Client` still has queued requests, the `Client` transitions to the **process.closing** state where it will complete all existing requests before firing the *done* event.\n  * The *done* event gracefully transitions the `Client` to the **destroyed** state.\n  * At any point in time, the *destroy* event will transition the `Client` from the **processing** state to the **destroyed** state, destroying any queued requests.\n* The **destroyed** state is a final state and the `Client` is no longer functional.\n\nA state diagram representing an Undici Client instance:\n\n```mermaid\nstateDiagram-v2\n  [*] --> idle\n  idle --> pending : connect\n  idle --> destroyed : destroy/close\n\n  pending --> idle : timeout\n  pending --> destroyed : destroy\n\n  state close_fork <<fork>>\n  pending --> close_fork : close\n  close_fork --> processing\n  close_fork --> destroyed\n\n  pending --> processing : process\n\n  processing --> pending : keepalive\n  processing --> destroyed : done\n  processing --> destroyed : destroy\n\n  destroyed --> [*]\n\n  state processing {\n      [*] --> running\n      running --> closing : close\n      running --> busy : needDrain\n      busy --> running : drainComplete\n      running --> [*] : keepalive\n      closing --> [*] : done\n  }\n```\n## State details\n\n### idle\n\nThe **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](Client.md#clientconnectoptions-callback), [`Client.pipeline()`](Client.md#clientpipelineoptions-handler), [`Client.request()`](Client.md#clientrequestoptions-callback), [`Client.stream()`](Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](/docs/docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](/docs/docs/api/Client.md#pending) and then most likely directly to [**processing**](/docs/docs/api/Client.md#processing).\n\nCalling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) or [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state since the `Client` instance will have no queued requests in this state.\n\n### pending\n\nThe **pending** state signifies a non-processing `Client`. Upon entering this state, the `Client` establishes a socket connection and emits the [`'connect'`](/docs/docs/api/Client.md#event-connect) event signalling a connection was successfully established with the `origin` provided during `Client` instantiation. The internal queue is initially empty, and requests can start queueing.\n\nCalling [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) with queued requests, transitions the `Client` to the [**processing**](/docs/docs/api/Client.md#processing) state. Without queued requests, it transitions to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state.\n\nCalling [`Client.destroy()`](/docs/docs/api/Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](/docs/docs/api/Client.md#destroyed) state regardless of existing requests.\n\n### processing\n\nThe **processing** state is a state machine within itself. It initializes to the [**processing.running**](/docs/docs/api/Client.md#running) state. The [`Client.dispatch()`](/docs/docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](Client.md#clientclosecallback), and [`Client.destroy()`](Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](/docs/docs/api/Client.md#closing) state. And `Client.destroy()` will transition to [**destroyed**](/docs/docs/api/Client.md#destroyed).\n\n#### running\n\nIn the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](/docs/docs/api/Client.md#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](/docs/docs/api/Client.md#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) nor [`Client.destroy()`](Client.md#clientdestroyerror-callback) are called, then the [**processing**](/docs/docs/api/Client.md#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](/docs/docs/api/Client.md#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](/docs/docs/api/Client.md#idle) state.\n\n#### busy\n\nThis sub-state is only entered when a request body is an instance of [Stream](https://nodejs.org/api/stream.html) and requires draining. The `Client` cannot process additional requests while in this state and must wait until the currently processing request body is completely drained before transitioning back to [**processing.running**](/docs/docs/api/Client.md#running).\n\n#### closing\n\nThis sub-state is only entered when a `Client` instance has queued requests and the [`Client.close()`](/docs/docs/api/Client.md#clientclosecallback) method is called. In this state, the `Client` instance continues to process requests as usual, with the one exception that no additional requests can be queued. Once all of the queued requests are processed, the `Client` will trigger the *done* event gracefully entering the [**destroyed**](/docs/docs/api/Client.md#destroyed) state without an error.\n\n### destroyed\n\nThe **destroyed** state is a final state for the `Client` instance. Once in this state, a `Client` is nonfunctional. Calling any other `Client` methods will result in an `ClientDestroyedError`.\n"
  },
  {
    "path": "docs/docs/best-practices/client-certificate.md",
    "content": "# Client certificate\n\nClient certificate authentication can be configured with the `Client`, the required options are passed along through the `connect` option.\n\nThe client certificates must be signed by a trusted CA. The Node.js default is to trust the well-known CAs curated by Mozilla.\n\nSetting the server option `requestCert: true` tells the server to request the client certificate.\n\nThe server option `rejectUnauthorized: false` allows us to handle any invalid certificate errors in client code. The `authorized` property on the socket of the incoming request will show if the client certificate was valid. The `authorizationError` property will give the reason if the certificate was not valid.\n\n### Client Certificate Authentication\n\n```js\nconst { readFileSync } = require('node:fs')\nconst { join } = require('node:path')\nconst { createServer } = require('node:https')\nconst { Client } = require('undici')\n\nconst serverOptions = {\n  ca: [\n    readFileSync(join(__dirname, 'client-ca-crt.pem'), 'utf8')\n  ],\n  key: readFileSync(join(__dirname, 'server-key.pem'), 'utf8'),\n  cert: readFileSync(join(__dirname, 'server-crt.pem'), 'utf8'),\n  requestCert: true,\n  rejectUnauthorized: false\n}\n\nconst server = createServer(serverOptions, (req, res) => {\n  // true if client cert is valid\n  if(req.client.authorized === true) {\n    console.log('valid')\n  } else {\n    console.error(req.client.authorizationError)\n  }\n  res.end()\n})\n\nserver.listen(0, function () {\n  const tls = {\n    ca: [\n      readFileSync(join(__dirname, 'server-ca-crt.pem'), 'utf8')\n    ],\n    key: readFileSync(join(__dirname, 'client-key.pem'), 'utf8'),\n    cert: readFileSync(join(__dirname, 'client-crt.pem'), 'utf8'),\n    rejectUnauthorized: false,\n    servername: 'agent1'\n  }\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: tls\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    body.on('data', (buf) => {})\n    body.on('end', () => {\n      client.close()\n      server.close()\n    })\n  })\n})\n```\n"
  },
  {
    "path": "docs/docs/best-practices/crawling.md",
    "content": "# Crawling\n\n[RFC 9309](https://datatracker.ietf.org/doc/html/rfc9309) defines crawlers as automated clients.\n\nSome web servers may reject requests that omit the `User-Agent` header or that use common defaults such as `'curl/7.79.1'`.\n\nIn **undici**, the default user agent is `'undici'`. Since undici is integrated into Node.js core as the implementation of `fetch()`, requests made via `fetch()` use `'node'` as the default user agent.\n\nIt is recommended to specify a **custom `User-Agent` header** when implementing crawlers. Providing a descriptive user agent allows servers to correctly identify the client and reduces the likelihood of requests being denied.\n\nA user agent string should include sufficient detail to identify the crawler and provide contact information. For example:\n\n```\nAcmeCo Crawler - acme.co - contact@acme.co\n```\n\nWhen adding contact details, avoid using personal identifiers such as your own name or a private email address—especially in a professional or employment context. Instead, use a role-based or organizational contact (e.g., crawler-team@company.com) to protect individual privacy while still enabling communication.\n\nIf a crawler behaves unexpectedly—for example, due to misconfiguration or implementation errors—server administrators can use the information in the user agent to contact the operator and coordinate an appropriate resolution.\n\nThe `User-Agent` header can be set on individual requests or applied globally by configuring a custom dispatcher.\n\n**Example: setting a `User-Agent` per request**\n\n```js\nimport { fetch } from 'undici'\n\nconst headers = {\n  'User-Agent': 'AcmeCo Crawler - acme.co - contact@acme.co'\n}\n\nconst res = await fetch('https://example.com', { headers })\n```\n\n## Best Practices for Crawlers\n\nWhen developing a crawler, the following practices are recommended in addition to setting a descriptive `User-Agent` header:\n\n* **Respect `robots.txt`**\n  Follow the directives defined in the target site’s `robots.txt` file, including disallowed paths and optional crawl-delay settings (see [W3C guidelines](https://www.w3.org/wiki/Write_Web_Crawler)).\n\n* **Rate limiting**\n  Regulate request frequency to avoid imposing excessive load on servers. Introduce delays between requests or limit the number of concurrent requests. The W3C suggests at least one second between requests.\n\n* **Error handling**\n  Implement retry logic with exponential backoff for transient failures, and stop requests when persistent errors occur (e.g., HTTP 403 or 429).\n\n* **Monitoring and logging**\n  Track request volume, response codes, and error rates to detect misbehavior and address issues proactively.\n\n* **Contact information**\n  Always include valid and current contact details in the `User-Agent` string so that administrators can reach the crawler operator if necessary.\n\n## References and Further Reading\n\n* [RFC 9309: The Robots Exclusion Protocol](https://datatracker.ietf.org/doc/html/rfc9309)\n* [W3C Wiki: Write Web Crawler](https://www.w3.org/wiki/Write_Web_Crawler)\n* [Ethical Web Crawling (WWW 2010 Conference Paper)](https://archives.iw3c2.org/www2010/proceedings/www/p1101.pdf)\n"
  },
  {
    "path": "docs/docs/best-practices/mocking-request.md",
    "content": "# Mocking Request\n\nUndici has its own mocking [utility](/docs/docs/api/MockAgent.md). It allow us to intercept undici HTTP requests and return mocked values instead. It can be useful for testing purposes.\n\nExample:\n\n```js\n// bank.mjs\nimport { request } from 'undici'\n\nexport async function bankTransfer(recipient, amount) {\n  const { body } = await request('http://localhost:3000/bank-transfer',\n    {\n      method: 'POST',\n      headers: {\n        'X-TOKEN-SECRET': 'SuperSecretToken',\n      },\n      body: JSON.stringify({\n        recipient,\n        amount\n      })\n    }\n  )\n  return await body.json()\n}\n```\n\nAnd this is what the test file looks like:\n\n```js\n// index.test.mjs\nimport { strict as assert } from 'node:assert'\nimport { MockAgent, setGlobalDispatcher, } from 'undici'\nimport { bankTransfer } from './bank.mjs'\n\nconst mockAgent = new MockAgent();\n\nsetGlobalDispatcher(mockAgent);\n\n// Provide the base url to the request\nconst mockPool = mockAgent.get('http://localhost:3000');\n\n// intercept the request\nmockPool.intercept({\n  path: '/bank-transfer',\n  method: 'POST',\n  headers: {\n    'X-TOKEN-SECRET': 'SuperSecretToken',\n  },\n  body: JSON.stringify({\n    recipient: '1234567890',\n    amount: '100'\n  })\n}).reply(200, {\n  message: 'transaction processed'\n})\n\nconst success = await bankTransfer('1234567890', '100')\n\nassert.deepEqual(success, { message: 'transaction processed' })\n\n// if you dont want to check whether the body or the headers contain the same value\n// just remove it from interceptor\nmockPool.intercept({\n  path: '/bank-transfer',\n  method: 'POST',\n}).reply(400, {\n  message: 'bank account not found'\n})\n\nconst badRequest = await bankTransfer('1234567890', '100')\n\nassert.deepEqual(badRequest, { message: 'bank account not found' })\n```\n\nExplore other MockAgent functionality [here](/docs/docs/api/MockAgent.md)\n\n## Access agent call history\n\nUsing a MockAgent also allows you to make assertions on the configuration used to make your request in your application.\n\nHere is an example :\n\n```js\n// index.test.mjs\nimport { strict as assert } from 'node:assert'\nimport { MockAgent, setGlobalDispatcher, fetch } from 'undici'\nimport { app } from './app.mjs'\n\n// given an application server running on http://localhost:3000\nawait app.start()\n\n// enable call history at instantiation\nconst mockAgent = new MockAgent({ enableCallHistory: true })\n// or after instantiation\nmockAgent.enableCallHistory()\n\nsetGlobalDispatcher(mockAgent)\n\n// this call is made (not intercepted)\nawait fetch(`http://localhost:3000/endpoint?query='hello'`, {\n  method: 'POST',\n  headers: { 'content-type': 'application/json' }\n  body: JSON.stringify({ data: '' })\n})\n\n// access to the call history of the MockAgent (which register every call made intercepted or not)\nassert.ok(mockAgent.getCallHistory()?.calls().length === 1)\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.fullUrl, `http://localhost:3000/endpoint?query='hello'`)\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.body, JSON.stringify({ data: '' }))\nassert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.searchParams, { query: 'hello' })\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.port, '3000')\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.host, 'localhost:3000')\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.method, 'POST')\nassert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.path, '/endpoint')\nassert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.headers, { 'content-type': 'application/json' })\n\n// clear all call history logs\nmockAgent.clearCallHistory()\n\nassert.ok(mockAgent.getCallHistory()?.calls().length === 0)\n```\n\nCalling `mockAgent.close()` will automatically clear and delete every call history for you.\n\nExplore other MockAgent functionality [here](/docs/docs/api/MockAgent.md)\n\nExplore other MockCallHistory functionality [here](/docs/docs/api/MockCallHistory.md)\n\nExplore other MockCallHistoryLog functionality [here](/docs/docs/api/MockCallHistoryLog.md)\n\n## Debug Mock Value\n\nWhen the interceptor and the request options are not the same, undici will automatically make a real HTTP request. To prevent real requests from being made, use `mockAgent.disableNetConnect()`:\n\n```js\nconst mockAgent = new MockAgent();\n\nsetGlobalDispatcher(mockAgent);\nmockAgent.disableNetConnect()\n\n// Provide the base url to the request\nconst mockPool = mockAgent.get('http://localhost:3000');\n\nmockPool.intercept({\n  path: '/bank-transfer',\n  method: 'POST',\n}).reply(200, {\n  message: 'transaction processed'\n})\n\nconst badRequest = await bankTransfer('1234567890', '100')\n// Will throw an error\n// MockNotMatchedError: Mock dispatch not matched for path '/bank-transfer':\n// subsequent request to origin http://localhost:3000 was not allowed (net.connect disabled)\n```\n\n## Reply with data based on request\n\nIf the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply`:\n\n```js\nmockPool.intercept({\n  path: '/bank-transfer',\n  method: 'POST',\n  headers: {\n    'X-TOKEN-SECRET': 'SuperSecretToken',\n  },\n  body: JSON.stringify({\n    recipient: '1234567890',\n    amount: '100'\n  })\n}).reply(200, (opts) => {\n  // do something with opts\n\n  return { message: 'transaction processed' }\n})\n```\n\nin this case opts will be\n\n```\n{\n  method: 'POST',\n  headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' },\n  body: '{\"recipient\":\"1234567890\",\"amount\":\"100\"}',\n  origin: 'http://localhost:3000',\n  path: '/bank-transfer'\n}\n```\n"
  },
  {
    "path": "docs/docs/best-practices/proxy.md",
    "content": "# Connecting through a proxy\n\nConnecting through a proxy is possible by:\n\n- Using [ProxyAgent](/docs/docs/api/ProxyAgent.md).\n- Configuring `Client` or `Pool` constructor.\n\nThe proxy url should be passed to the `Client` or `Pool` constructor, while the upstream server url\nshould be added to every request call in the `path`.\nFor instance, if you need to send a request to the `/hello` route of your upstream server,\nthe `path` should be `path: 'http://upstream.server:port/hello?foo=bar'`.\n\nIf you proxy requires basic authentication, you can send it via the `proxy-authorization` header.\n\n### Connect without authentication\n\n```js\nimport { Client } from 'undici'\nimport { createServer } from 'http'\nimport { createProxy } from 'proxy'\n\nconst server = await buildServer()\nconst proxyServer = await buildProxy()\n\nconst serverUrl = `http://localhost:${server.address().port}`\nconst proxyUrl = `http://localhost:${proxyServer.address().port}`\n\nserver.on('request', (req, res) => {\n  console.log(req.url) // '/hello?foo=bar'\n  res.setHeader('content-type', 'application/json')\n  res.end(JSON.stringify({ hello: 'world' }))\n})\n\nconst client = new Client(proxyUrl)\n\nconst response = await client.request({\n  method: 'GET',\n  path: serverUrl + '/hello?foo=bar'\n})\n\nresponse.body.setEncoding('utf8')\nlet data = ''\nfor await (const chunk of response.body) {\n  data += chunk\n}\nconsole.log(response.statusCode) // 200\nconsole.log(JSON.parse(data)) // { hello: 'world' }\n\nserver.close()\nproxyServer.close()\nclient.close()\n\nfunction buildServer () {\n  return new Promise((resolve, reject) => {\n    const server = createServer()\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildProxy () {\n  return new Promise((resolve, reject) => {\n    const server = createProxy(createServer())\n    server.listen(0, () => resolve(server))\n  })\n}\n```\n\n### Connect with authentication\n\n```js\nimport { Client } from 'undici'\nimport { createServer } from 'http'\nimport { createProxy } from 'proxy'\n\nconst server = await buildServer()\nconst proxyServer = await buildProxy()\n\nconst serverUrl = `http://localhost:${server.address().port}`\nconst proxyUrl = `http://localhost:${proxyServer.address().port}`\n\nproxyServer.authenticate = function (req) {\n  return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n}\n\nserver.on('request', (req, res) => {\n  console.log(req.url) // '/hello?foo=bar'\n  res.setHeader('content-type', 'application/json')\n  res.end(JSON.stringify({ hello: 'world' }))\n})\n\nconst client = new Client(proxyUrl)\n\nconst response = await client.request({\n  method: 'GET',\n  path: serverUrl + '/hello?foo=bar',\n  headers: {\n    'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n})\n\nresponse.body.setEncoding('utf8')\nlet data = ''\nfor await (const chunk of response.body) {\n  data += chunk\n}\nconsole.log(response.statusCode) // 200\nconsole.log(JSON.parse(data)) // { hello: 'world' }\n\nserver.close()\nproxyServer.close()\nclient.close()\n\nfunction buildServer () {\n  return new Promise((resolve, reject) => {\n    const server = createServer()\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildProxy () {\n  return new Promise((resolve, reject) => {\n    const server = createProxy(createServer())\n    server.listen(0, () => resolve(server))\n  })\n}\n```\n\n"
  },
  {
    "path": "docs/docs/best-practices/undici-vs-builtin-fetch.md",
    "content": "# Undici Module vs. Node.js Built-in Fetch\n\nNode.js has shipped a built-in `fetch()` implementation powered by undici since\nNode.js v18. This guide explains the relationship between the `undici` npm\npackage and the built-in `fetch`, and when you should install one versus relying\non the other.\n\n## Background\n\nThe `fetch()`, `Request`, `Response`, `Headers`, and `FormData` globals in\nNode.js v18+ are provided by a version of undici that is bundled into Node.js\nitself. You can check which version is bundled with:\n\n```js\nconsole.log(process.versions.undici); // e.g., \"7.5.0\"\n```\n\nWhen you install undici from npm, you get the full library with all of its\nadditional APIs, and potentially a newer release than what your Node.js version\nbundles.\n\n## When you do NOT need to install undici\n\nIf all of the following are true, you can rely on the built-in globals and skip\nadding undici to your dependencies:\n\n- You only need the standard Fetch API (`fetch`, `Request`, `Response`,\n  `Headers`, `FormData`).\n- You are running Node.js v18 or later.\n- You do not depend on features or bug fixes introduced in a version of undici\n  newer than the one bundled with your Node.js release.\n- You want zero additional runtime dependencies.\n- You want cross-platform interoperability with browsers and other runtimes\n  (Deno, Bun, Cloudflare Workers, etc.) using the same Fetch API surface.\n\nThis is common in applications that make straightforward HTTP requests or in\nlibraries that target multiple JavaScript runtimes.\n\n## When you SHOULD install undici\n\nInstall undici from npm when you need capabilities beyond the standard Fetch API:\n\n### Advanced HTTP APIs\n\nundici exposes `request`, `stream`, `pipeline`, and `connect` methods that\nprovide lower-level control and significantly better performance than `fetch`:\n\n```js\nimport { request } from 'undici';\n\nconst { statusCode, headers, body } = await request('https://example.com');\nconst data = await body.json();\n```\n\n### Connection pooling and dispatchers\n\n`Client`, `Pool`, `BalancedPool`, `Agent`, and their configuration options\nlet you manage connection lifecycle, keep-alive behavior, pipelining depth,\nand concurrency limits:\n\n```js\nimport { Pool } from 'undici';\n\nconst pool = new Pool('https://example.com', { connections: 10 });\nconst { body } = await pool.request({ path: '/', method: 'GET' });\n```\n\n### Proxy support\n\n`ProxyAgent` and `EnvHttpProxyAgent` handle HTTP(S) proxying. Note that\nNode.js v22.21.0+ and v24.0.0+ support environment-variable-based proxy\nconfiguration for the built-in `fetch` via the `--use-env-proxy` flag (or\n`NODE_USE_ENV_PROXY=1`). However, undici's `ProxyAgent` still provides\nprogrammatic control through the dispatcher API:\n\n```js\nimport { ProxyAgent, fetch } from 'undici';\n\nconst proxyAgent = new ProxyAgent('https://my-proxy.example.com:8080');\nconst response = await fetch('https://example.com', { dispatcher: proxyAgent });\n```\n\n### Testing and mocking\n\n`MockAgent`, `MockClient`, and `MockPool` let you intercept and mock HTTP\nrequests without patching globals or depending on external libraries:\n\n```js\nimport { MockAgent, setGlobalDispatcher, fetch } from 'undici';\n\nconst mockAgent = new MockAgent();\nsetGlobalDispatcher(mockAgent);\n\nconst pool = mockAgent.get('https://example.com');\npool.intercept({ path: '/api' }).reply(200, { message: 'mocked' });\n```\n\n### Interceptors and middleware\n\nCustom dispatchers and interceptors (retry, redirect, cache, DNS) give you\nfine-grained control over how requests are processed.\n\n### Newer version than what Node.js bundles\n\nThe npm package often includes features, performance improvements, and bug fixes\nthat have not yet landed in a Node.js release. If you need a specific fix or\nfeature, you can install a newer version directly.\n\n## Version compatibility\n\n| Node.js version | Bundled undici version | Notes |\n|---|---|---|\n| v18.x | ~5.x | `fetch` is experimental (behind `--experimental-fetch` in early v18) |\n| v20.x | ~6.x | `fetch` is stable |\n| v22.x | ~6.x / ~7.x | `fetch` is stable |\n| v24.x | ~7.x | `fetch` is stable; env-proxy support via `--use-env-proxy` |\n\nYou can always check the exact bundled version at runtime with\n`process.versions.undici`.\n\nInstalling undici from npm does not replace the built-in globals. If you want\nyour installed version to override the global `fetch`, use\n[`setGlobalDispatcher`](/docs/api/GlobalInstallation.md) or import `fetch`\ndirectly from `'undici'`:\n\n```js\nimport { fetch } from 'undici'; // uses your installed version, not the built-in\n```\n\n## Further reading\n\n- [API Reference: Fetch](/docs/api/Fetch.md)\n- [API Reference: Client](/docs/api/Client.md)\n- [API Reference: Pool](/docs/api/Pool.md)\n- [API Reference: ProxyAgent](/docs/api/ProxyAgent.md)\n- [API Reference: MockAgent](/docs/api/MockAgent.md)\n- [API Reference: Global Installation](/docs/api/GlobalInstallation.md)\n"
  },
  {
    "path": "docs/docs/best-practices/writing-tests.md",
    "content": "# Writing tests\n\nUndici is tuned for a production use case and its default will keep\na socket open for a few seconds after an HTTP request is completed to\nremove the overhead of opening up a new socket. These settings that makes\nUndici shine in production are not a good fit for using Undici in automated\ntests, as it will result in longer execution times.\n\nThe following are good defaults that will keep the socket open for only 10ms:\n\n```js\nimport { request, setGlobalDispatcher, Agent } from 'undici'\n\nconst agent = new Agent({\n  keepAliveTimeout: 10, // milliseconds\n  keepAliveMaxTimeout: 10 // milliseconds\n})\n\nsetGlobalDispatcher(agent)\n```\n\n## Guarding against unexpected disconnects\n\nUndici's `Client` automatically reconnects after a socket error. This means\na test can silently disconnect, reconnect, and still pass. Unfortunately, \nthis could mask bugs like unexpected parser errors or protocol violations.\nTo catch these silent reconnections, add a disconnect guard after creating \na `Client`:\n\n```js\nconst { Client } = require('undici')\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\n\ntest('example with disconnect guard', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const client = new Client('http://localhost:3000')\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  // ... test logic ...\n})\n```\n\n`client.close()` and `client.destroy()` both emit `'disconnect'` events, but\nthose are expected. The guard only fails when a disconnect happens during the\nactive test (i.e., `!client.closed && !client.destroyed` is true).\n\nSkip the guard for tests where a disconnect is expected behavior, such as:\n\n- Signal aborts (`signal.emit('abort')`, `ac.abort()`)\n- Server-side destruction (`res.destroy()`, `req.socket.destroy()`)\n- Client-side body destruction mid-stream (`data.body.destroy()`)\n- Timeout errors (`HeadersTimeoutError`, `BodyTimeoutError`)\n- Successful upgrades (the socket is detached from the `Client`)\n- Retry/reconnect tests where the disconnect triggers the retry\n- HTTP parser errors from malformed responses (`HTTPParserError`)\n"
  },
  {
    "path": "docs/docsify/sidebar.md",
    "content": "<!-- Sidebar for Docsify -->\n\n* [**Home**](/ \"Node.js Undici\")\n* API\n  * [Dispatcher](/docs/api/Dispatcher.md \"Undici API - Dispatcher\")\n  * [Client](/docs/api/Client.md \"Undici API - Client\")\n  * [H2CClient](/docs/api/H2CClient.md \"Undici H2C API - Client\")\n  * [Pool](/docs/api/Pool.md \"Undici API - Pool\")\n  * [BalancedPool](/docs/api/BalancedPool.md \"Undici API - BalancedPool\")\n  * [RoundRobinPool](/docs/api/RoundRobinPool.md \"Undici API - RoundRobinPool\")\n  * [Agent](/docs/api/Agent.md \"Undici API - Agent\")\n  * [ProxyAgent](/docs/api/ProxyAgent.md \"Undici API - ProxyAgent\")\n  * [Socks5Agent](/docs/api/Socks5Agent.md \"Undici API - SOCKS5 Agent\")\n  * [RetryAgent](/docs/api/RetryAgent.md \"Undici API - RetryAgent\")\n  * [Connector](/docs/api/Connector.md \"Custom connector\")\n  * [Errors](/docs/api/Errors.md \"Undici API - Errors\")\n  * [EventSource](/docs/api/EventSource.md \"Undici API - EventSource\")\n  * [Fetch](/docs/api/Fetch.md \"Undici API - Fetch\")\n  * [Global Installation](/docs/api/GlobalInstallation.md \"Undici API - Global Installation\")\n  * [Cookies](/docs/api/Cookies.md \"Undici API - Cookies\")\n  * [MockClient](/docs/api/MockClient.md \"Undici API - MockClient\")\n  * [MockPool](/docs/api/MockPool.md \"Undici API - MockPool\")\n  * [MockAgent](/docs/api/MockAgent.md \"Undici API - MockAgent\")\n  * [SnapshotAgent](/docs/api/SnapshotAgent.md \"Undici API - SnapshotAgent\")\n  * [MockCallHistory](/docs/api/MockCallHistory.md \"Undici API - MockCallHistory\")\n  * [MockCallHistoryLog](/docs/api/MockCallHistoryLog.md \"Undici API - MockCallHistoryLog\")\n  * [MockErrors](/docs/api/MockErrors.md \"Undici API - MockErrors\")\n  * [API Lifecycle](/docs/api/api-lifecycle.md \"Undici API - Lifecycle\")\n  * [Diagnostics Channel Support](/docs/api/DiagnosticsChannel.md \"Diagnostics Channel Support\")\n  * [Debug](/docs/api/Debug.md \"Undici API - Debugging Undici\")\n  * [WebSocket](/docs/api/WebSocket.md \"Undici API - WebSocket\")\n  * [MIME Type Parsing](/docs/api/ContentType.md \"Undici API - MIME Type Parsing\")\n  * [CacheStorage](/docs/api/CacheStorage.md \"Undici API - CacheStorage\")\n  * [Util](/docs/api/Util.md \"Undici API - Util\")\n  * [RedirectHandler](/docs/api/RedirectHandler.md \"Undici API - RedirectHandler\")\n  * [RetryHandler](/docs/api/RetryHandler.md \"Undici API - RetryHandler\")\n  * [DiagnosticsChannel](/docs/api/DiagnosticsChannel.md \"Undici API - DiagnosticsChannel\")\n  * [EnvHttpProxyAgent](/docs/api/EnvHttpProxyAgent.md \"Undici API - EnvHttpProxyAgent\")\n  * [PoolStats](/docs/api/PoolStats.md \"Undici API - PoolStats\")\n* Examples\n  * [Undici Examples](/examples/ \"Undici Examples\")\n* Best Practices\n  * [Undici vs. Built-in Fetch](/docs/best-practices/undici-vs-builtin-fetch.md \"When to install undici vs using Node.js built-in fetch\")\n  * [Proxy](/docs/best-practices/proxy.md \"Connecting through a proxy\")\n  * [Client Certificate](/docs/best-practices/client-certificate.md \"Connect using a client certificate\")\n  * [Writing Tests](/docs/best-practices/writing-tests.md \"Using Undici inside tests\")\n  * [Mocking Request](/docs/best-practices/mocking-request.md \"Using Undici inside tests\")\n  * [Crawling](/docs/best-practices/crawling.md \"Crawling\")\n"
  },
  {
    "path": "docs/examples/README.md",
    "content": "\n## undici.request() examples\n\n### A simple GET request, read the response body as text:\n```js\nconst { request } = require('undici')\nasync function getRequest (port = 3001) {\n  // A simple GET request\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(`http://localhost:${port}/`)\n\n  const data = await body.text()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', data)\n}\n```\n\n### A JSON POST request, read the response body as json:\n```js\nconst { request } = require('undici')\nasync function postJSONRequest (port = 3001) {\n  const requestBody = {\n    hello: 'JSON POST Example body'\n  }\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/json`,\n    { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(requestBody) }\n  )\n\n  // .json() will fail if we did not receive a valid json body in response:\n  const decodedJson = await body.json()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', decodedJson)\n}\n```\n\n### A Form POST request, read the response body as text:\n```js\nconst { request } = require('undici')\nasync function postFormRequest (port = 3001) {\n  // Make a URL-encoded form POST request:\n  const qs = require('node:querystring')\n\n  const requestBody = {\n    hello: 'URL Encoded Example body'\n  }\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/form`,\n    { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: qs.stringify(requestBody) }\n  )\n\n  const data = await body.text()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', data)\n}\n```\n\n### A FormData request with file stream, read the response body as text\n\n```js\nconst { request } = require('undici')\nconst { openAsBlob } = require('fs')\n\nasync function formDataBlobRequest () {\n  // Make a FormData request with file stream:\n\n  const formData = new FormData()\n  formData.append('field', 42)\n  formData.set('file', await openAsBlob('./index.mjs'))\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request('http://127.0.0.1:3000', {\n    method: 'POST',\n    body: formData\n  })\n\n  const data = await body.text()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', data)\n}\n\n```\n\n### A DELETE request\n```js\nconst { request } = require('undici')\nasync function deleteRequest (port = 3001) {\n  // Make a DELETE request\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/something`,\n    { method: 'DELETE' }\n  )\n\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  // For a DELETE request we expect a 204 response with no body if successful, in which case getting the body content with .json() will fail\n  if (statusCode === 204) {\n    console.log('delete successful')\n    // always consume the body if there is one:\n    await body.dump()\n  } else {\n    const data = await body.text()\n    console.log('received unexpected data', data)\n  }\n}\n```\n\n## Production configuration\n\n### Using interceptors to add response caching, DNS lookup caching and connection retries\n\n```js\nimport { Agent, interceptors, setGlobalDispatcher } from 'undici'\n\n// Interceptors to add response caching, DNS caching and retrying to the dispatcher\nconst { cache, dns, retry } = interceptors\n\nconst defaultDispatcher = new Agent({\n  connections: 100, // Limit concurrent kept-alive connections to not run out of resources\n  headersTimeout: 10_000, // 10 seconds; set as appropriate for the remote servers you plan to connect to\n  bodyTimeout: 10_000,\n}).compose(cache(), dns(), retry())\n\nsetGlobalDispatcher(defaultDispatcher) // Add these interceptors to all `fetch` and Undici `request` calls\n```\n\n### Cache interceptor with `fetch`\n\n```js\nimport { Agent, interceptors, setGlobalDispatcher, fetch } from 'undici'\nimport { createServer } from 'node:http'\n\nconst { cache } = interceptors\n\nconst server = createServer((req, res) => {\n  // Cache this response for 60 seconds\n  res.setHeader('cache-control', 'public, max-age=60')\n  res.end(JSON.stringify({ now: Date.now() }))\n})\n\nawait new Promise((resolve) => server.listen(0, resolve))\nconst { port } = server.address()\n\nconst dispatcher = new Agent().compose(cache())\nsetGlobalDispatcher(dispatcher)\n\n// First request goes to the origin server\nconst first = await fetch(`http://localhost:${port}`)\nconst firstBody = await first.json()\n\n// Second request is served from cache if still fresh\nconst second = await fetch(`http://localhost:${port}`)\nconst secondBody = await second.json()\n\nconsole.log(firstBody.now === secondBody.now) // true\n\nawait dispatcher.close()\nawait new Promise((resolve) => server.close(resolve))\n```\n\n## Connecting via Unix domain sockets (UDS)\n\n### request() over UDS (per-call dispatcher)\n```js\nconst { Agent, request } = require('undici')\n\nasync function requestOverUds () {\n  const uds = new Agent({ connect: { socketPath: '/var/run/docker.sock' } })\n  try {\n    const { statusCode, headers, body } = await request('http://localhost/_ping', {\n      dispatcher: uds\n    })\n    console.log(statusCode, headers, await body.text())\n  } finally {\n    await uds.close()\n  }\n}\n```\n\n### fetch() over UDS (per-call dispatcher)\n```js\nconst { Agent, fetch } = require('undici')\n\nasync function fetchOverUds () {\n  const uds = new Agent({ connect: { socketPath: '/var/run/docker.sock' } })\n  try {\n    const res = await fetch('http://localhost/containers/json', { dispatcher: uds })\n    console.log(res.status, await res.text())\n  } finally {\n    await uds.close()\n  }\n}\n```\n\n> Note\n> - `connect.socketPath` must be the exact filesystem path your server listens on (e.g., Docker: `/var/run/docker.sock`)..\n> - Not supported on Windows (uses named pipes instead)."
  },
  {
    "path": "docs/examples/ca-fingerprint/index.js",
    "content": "'use strict'\n\nconst crypto = require('node:crypto')\nconst https = require('node:https')\nconst { Client, buildConnector } = require('../../../')\nconst pem = require('@metcoder95/https-pem')\n\nconst caFingerprint = getFingerprint(pem.cert.toString()\n  .split('\\n')\n  .slice(1, -1)\n  .map(line => line.trim())\n  .join('')\n)\n\nconst server = https.createServer(pem, (req, res) => {\n  res.setHeader('Content-Type', 'text/plain')\n  res.end('hello')\n})\n\nserver.listen(0, function () {\n  const connector = buildConnector({ rejectUnauthorized: false })\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect (opts, cb) {\n      connector(opts, (err, socket) => {\n        if (err) {\n          cb(err)\n        } else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {\n          socket.destroy()\n          cb(new Error('Fingerprint does not match or malformed certificate'))\n        } else {\n          cb(null, socket)\n        }\n      })\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    if (err) throw err\n\n    const bufs = []\n    data.body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    data.body.on('end', () => {\n      console.log(Buffer.concat(bufs).toString('utf8'))\n      client.close()\n      server.close()\n    })\n  })\n})\n\nfunction getIssuerCertificate (socket) {\n  let certificate = socket.getPeerCertificate(true)\n  while (certificate && Object.keys(certificate).length > 0) {\n    // invalid certificate\n    if (certificate.issuerCertificate == null) {\n      return null\n    }\n\n    // We have reached the root certificate.\n    // In case of self-signed certificates, `issuerCertificate` may be a circular reference.\n    if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {\n      break\n    }\n\n    // continue the loop\n    certificate = certificate.issuerCertificate\n  }\n  return certificate\n}\n\nfunction getFingerprint (content, inputEncoding = 'base64', outputEncoding = 'hex') {\n  const shasum = crypto.createHash('sha256')\n  shasum.update(content, inputEncoding)\n  const res = shasum.digest(outputEncoding)\n  return res.toUpperCase().match(/.{1,2}/g).join(':')\n}\n"
  },
  {
    "path": "docs/examples/eventsource.js",
    "content": "'use strict'\n\nconst { randomBytes } = require('node:crypto')\nconst { EventSource } = require('../../')\n\nasync function main () {\n  const url = `https://smee.io/${randomBytes(8).toString('base64url')}`\n  console.log(`Connecting to event source server ${url}`)\n  const ev = new EventSource(url)\n  ev.onmessage = console.log\n  ev.onerror = console.log\n  ev.onopen = console.log\n\n  // Special event of smee.io\n  ev.addEventListener('ready', console.log)\n\n  // Ping event is sent every 30 seconds by smee.io\n  ev.addEventListener('ping', console.log)\n}\nmain()\n"
  },
  {
    "path": "docs/examples/fetch.js",
    "content": "'use strict'\n\nconst { fetch } = require('../../')\n\nasync function main () {\n  const res = await fetch('http://localhost:3001/')\n\n  const data = await res.text()\n  console.log('response received', res.status)\n  console.log('headers', res.headers)\n  console.log('data', data)\n}\nmain()\n"
  },
  {
    "path": "docs/examples/proxy/fetch.mjs",
    "content": "import * as http from 'node:http'\nimport { once } from 'node:events'\nimport { createProxy } from 'proxy'\nimport { fetch, ProxyAgent } from '../../../'\n\nconst proxyServer = createProxy(http.createServer())\nconst server = http.createServer((req, res) => {\n  res.writeHead(200, { 'Content-Type': 'text/plain' })\n  res.end('okay')\n})\n\nproxyServer.on('request', (req, res) => {\n  console.log(`Incoming request to ${req.url}`)\n})\n\nawait once(proxyServer.listen(0), 'listening')\nawait once(server.listen(0), 'listening')\n\nconst { port: proxyPort } = proxyServer.address()\nconst { port } = server.address()\n\nconsole.log(`Proxy listening on port ${proxyPort}`)\nconsole.log(`Server listening on port ${port}`)\ntry {\n  // undici does a tunneling to the proxy server using CONNECT.\n  const agent = new ProxyAgent(`http://localhost:${proxyPort}`)\n  const response = await fetch(`http://localhost:${port}`, {\n    dispatcher: agent,\n    method: 'GET'\n  })\n  const data = await response.text()\n  console.log('Response data:', data)\n} catch (e) {\n  console.log(e)\n}\n"
  },
  {
    "path": "docs/examples/proxy/index.js",
    "content": "'use strict'\n\nconst { Pool, Client } = require('../../../')\nconst http = require('node:http')\nconst proxy = require('./proxy')\n\nconst pool = new Pool('http://localhost:4001', {\n  connections: 256,\n  pipelining: 1\n})\n\nasync function run () {\n  await Promise.all([\n    new Promise(resolve => {\n      // Proxy\n      http.createServer((req, res) => {\n        proxy({ req, res, proxyName: 'example' }, pool).catch(err => {\n          if (res.headersSent) {\n            res.destroy(err)\n          } else {\n            for (const name of res.getHeaderNames()) {\n              res.removeHeader(name)\n            }\n            res.statusCode = err.statusCode || 500\n            res.end()\n          }\n        })\n      }).listen(4000, resolve)\n    }),\n    new Promise(resolve => {\n      // Upstream\n      http.createServer((req, res) => {\n        res.end('hello world')\n      }).listen(4001, resolve)\n    })\n  ])\n\n  const client = new Client('http://localhost:4000')\n  const { body } = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  for await (const chunk of body) {\n    console.log(String(chunk))\n  }\n}\n\nrun()\n"
  },
  {
    "path": "docs/examples/proxy/proxy.js",
    "content": "'use strict'\n\nconst net = require('node:net')\nconst { pipeline } = require('node:stream')\nconst { STATUS_CODES } = require('node:http')\n\nmodule.exports = async function proxy (ctx, client) {\n  const { req, socket, proxyName } = ctx\n\n  const headers = getHeaders({\n    headers: req.rawHeaders,\n    httpVersion: req.httpVersion,\n    socket: req.socket,\n    proxyName\n  })\n\n  if (socket) {\n    const handler = new WSHandler(ctx)\n    client.dispatch({\n      method: req.method,\n      path: req.url,\n      headers,\n      upgrade: 'Websocket'\n    }, handler)\n    return handler.promise\n  } else {\n    const handler = new HTTPHandler(ctx)\n    client.dispatch({\n      method: req.method,\n      path: req.url,\n      headers,\n      body: req\n    }, handler)\n    return handler.promise\n  }\n}\n\nclass HTTPHandler {\n  constructor (ctx) {\n    const { req, res, proxyName } = ctx\n\n    this.proxyName = proxyName\n    this.req = req\n    this.res = res\n    this.resume = null\n    this.abort = null\n    this.promise = new Promise((resolve, reject) => {\n      this.callback = err => err ? reject(err) : resolve()\n    })\n  }\n\n  onConnect (abort) {\n    if (this.req.aborted) {\n      abort()\n    } else {\n      this.abort = abort\n      this.res.on('close', abort)\n    }\n  }\n\n  onHeaders (statusCode, headers, resume) {\n    if (statusCode < 200) {\n      return\n    }\n\n    this.resume = resume\n    this.res.on('drain', resume)\n    this.res.writeHead(statusCode, getHeaders({\n      headers,\n      proxyName: this.proxyName,\n      httpVersion: this.httpVersion\n    }))\n  }\n\n  onData (chunk) {\n    return this.res.write(chunk)\n  }\n\n  onComplete () {\n    this.res.off('close', this.abort)\n    this.res.off('drain', this.resume)\n\n    this.res.end()\n    this.callback()\n  }\n\n  onError (err) {\n    this.res.off('close', this.abort)\n    this.res.off('drain', this.resume)\n\n    this.callback(err)\n  }\n}\n\nclass WSHandler {\n  constructor (ctx) {\n    const { req, socket, proxyName, head } = ctx\n\n    setupSocket(socket)\n\n    this.proxyName = proxyName\n    this.httpVersion = req.httpVersion\n    this.socket = socket\n    this.head = head\n    this.abort = null\n    this.promise = new Promise((resolve, reject) => {\n      this.callback = err => err ? reject(err) : resolve()\n    })\n  }\n\n  onConnect (abort) {\n    if (this.socket.destroyed) {\n      abort()\n    } else {\n      this.abort = abort\n      this.socket.on('close', abort)\n    }\n  }\n\n  onUpgrade (statusCode, headers, socket) {\n    this.socket.off('close', this.abort)\n\n    // TODO: Check statusCode?\n\n    if (this.head && this.head.length) {\n      socket.unshift(this.head)\n    }\n\n    setupSocket(socket)\n\n    headers = getHeaders({\n      headers,\n      proxyName: this.proxyName,\n      httpVersion: this.httpVersion\n    })\n\n    let head = ''\n    for (let n = 0; n < headers.length; n += 2) {\n      head += `\\r\\n${headers[n]}: ${headers[n + 1]}`\n    }\n\n    this.socket.write(`HTTP/1.1 101 Switching Protocols\\r\\nconnection: upgrade\\r\\nupgrade: websocket${head}\\r\\n\\r\\n`)\n\n    pipeline(socket, this.socket, socket, this.callback)\n  }\n\n  onError (err) {\n    this.socket.off('close', this.abort)\n\n    this.callback(err)\n  }\n}\n\n// This expression matches hop-by-hop headers.\n// These headers are meaningful only for a single transport-level connection,\n// and must not be retransmitted by proxies or cached.\nconst HOP_EXPR = /^(te|host|upgrade|trailers|connection|keep-alive|http2-settings|transfer-encoding|proxy-connection|proxy-authenticate|proxy-authorization)$/i\n\n// Removes hop-by-hop and pseudo headers.\n// Updates via and forwarded headers.\n// Only hop-by-hop headers may be set using the Connection general header.\nfunction getHeaders ({\n  headers,\n  proxyName,\n  httpVersion,\n  socket\n}) {\n  let via = ''\n  let forwarded = ''\n  let host = ''\n  let authority = ''\n  let connection = ''\n\n  for (let n = 0; n < headers.length; n += 2) {\n    const key = headers[n]\n    const val = headers[n + 1]\n\n    if (!via && key.length === 3 && key.toLowerCase() === 'via') {\n      via = val\n    } else if (!host && key.length === 4 && key.toLowerCase() === 'host') {\n      host = val\n    } else if (!forwarded && key.length === 9 && key.toLowerCase() === 'forwarded') {\n      forwarded = val\n    } else if (!connection && key.length === 10 && key.toLowerCase() === 'connection') {\n      connection = val\n    } else if (!authority && key.length === 10 && key === ':authority') {\n      authority = val\n    }\n  }\n\n  let remove\n  if (connection && !HOP_EXPR.test(connection)) {\n    remove = connection.split(/,\\s*/)\n  }\n\n  const result = []\n  for (let n = 0; n < headers.length; n += 2) {\n    const key = headers[n]\n    const val = headers[n + 1]\n\n    if (\n      key.charAt(0) !== ':' &&\n      !HOP_EXPR.test(key) &&\n      (!remove || !remove.includes(key))\n    ) {\n      result.push(key, val)\n    }\n  }\n\n  if (socket) {\n    result.push('forwarded', (forwarded ? forwarded + ', ' : '') + [\n      `by=${printIp(socket.localAddress, socket.localPort)}`,\n      `for=${printIp(socket.remoteAddress, socket.remotePort)}`,\n      `proto=${socket.encrypted ? 'https' : 'http'}`,\n      `host=${printIp(authority || host || '')}`\n    ].join(';'))\n  } else if (forwarded) {\n    // The forwarded header should not be included in response.\n    throw new BadGateway()\n  }\n\n  if (proxyName) {\n    if (via) {\n      if (via.split(',').some(name => name.endsWith(proxyName))) {\n        throw new LoopDetected()\n      }\n      via += ', '\n    }\n    via += `${httpVersion} ${proxyName}`\n  }\n\n  if (via) {\n    result.push('via', via)\n  }\n\n  return result\n}\n\nfunction setupSocket (socket) {\n  socket.setTimeout(0)\n  socket.setNoDelay(true)\n  socket.setKeepAlive(true, 0)\n}\n\nfunction printIp (address, port) {\n  const isIPv6 = net.isIPv6(address)\n  let str = `${address}`\n  if (isIPv6) {\n    str = `[${str}]`\n  }\n  if (port) {\n    str = `${str}:${port}`\n  }\n  if (isIPv6 || port) {\n    str = `\"${str}\"`\n  }\n  return str\n}\n\nclass BadGateway extends Error {\n  constructor (message = STATUS_CODES[502]) {\n    super(message)\n  }\n\n  toString () {\n    return `BadGatewayError: ${this.message}`\n  }\n\n  get name () {\n    return 'BadGatewayError'\n  }\n\n  get status () {\n    return 502\n  }\n\n  get statusCode () {\n    return 502\n  }\n\n  get expose () {\n    return false\n  }\n\n  get headers () {\n    return undefined\n  }\n}\n\nclass LoopDetected extends Error {\n  constructor (message = STATUS_CODES[508]) {\n    super(message)\n  }\n\n  toString () {\n    return `LoopDetectedError: ${this.message}`\n  }\n\n  get name () {\n    return 'LoopDetectedError'\n  }\n\n  get status () {\n    return 508\n  }\n\n  get statusCode () {\n    return 508\n  }\n\n  get expose () {\n    return false\n  }\n\n  get headers () {\n    return undefined\n  }\n}\n"
  },
  {
    "path": "docs/examples/proxy/websocket.js",
    "content": "'use strict'\n\nconst { Pool, Client } = require('../../../')\nconst http = require('node:http')\nconst proxy = require('./proxy')\nconst WebSocket = require('ws')\n\nconst pool = new Pool('http://localhost:4001', {\n  connections: 256,\n  pipelining: 1\n})\n\nfunction createWebSocketServer () {\n  const wss = new WebSocket.Server({ noServer: true })\n\n  wss.on('connection', ws => {\n    ws.on('message', message => {\n      console.log(`Received message: ${message}`)\n      ws.send('Received your message!')\n    })\n  })\n\n  return wss\n}\n\nasync function run () {\n  await Promise.all([\n    new Promise(resolve => {\n      // Proxy\n      http.createServer((req, res) => {\n        proxy({ req, res, proxyName: 'example' }, pool).catch(err => {\n          if (res.headersSent) {\n            res.destroy(err)\n          } else {\n            for (const name of res.getHeaderNames()) {\n              res.removeHeader(name)\n            }\n            res.statusCode = err.statusCode || 500\n            res.end()\n          }\n        })\n      }).listen(4000, resolve)\n    }),\n    new Promise(resolve => {\n      // Upstream\n      http.createServer((req, res) => {\n        res.end('hello world')\n      }).listen(4001, resolve)\n    }),\n    new Promise(resolve => {\n      // WebSocket server\n      const server = http.createServer((req, res) => {\n        res.writeHead(200, { 'Content-Type': 'text/plain' })\n        res.end('WebSocket server is running!')\n      })\n\n      const wss = createWebSocketServer()\n\n      server.on('upgrade', (request, socket, head) => {\n        wss.handleUpgrade(request, socket, head, ws => {\n          wss.emit('connection', ws, request)\n        })\n      })\n\n      server.listen(4002, resolve)\n    })\n  ])\n\n  const client = new Client('http://localhost:4000')\n  const { body } = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  for await (const chunk of body) {\n    console.log(String(chunk))\n  }\n\n  // WebSocket client\n  const ws = new WebSocket('ws://localhost:4002')\n  ws.on('open', () => {\n    ws.send('Hello, WebSocket Server!')\n  })\n\n  ws.on('message', message => {\n    console.log(`WebSocket Server says: ${message}`)\n    ws.close()\n  })\n}\n\nrun()\n"
  },
  {
    "path": "docs/examples/proxy-agent.js",
    "content": "'use strict'\n\nconst { request, setGlobalDispatcher, ProxyAgent } = require('../..')\n\nsetGlobalDispatcher(new ProxyAgent('http://localhost:8000/'))\n\nasync function main () {\n  const {\n    statusCode,\n    headers,\n    trailers,\n    body\n    // send the request via the http://localhost:8000/ HTTP proxy\n  } = await request('http://localhost:3000/undici')\n\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n\n  for await (const data of body) {\n    console.log('data', data)\n  }\n\n  console.log('trailers', trailers)\n}\nmain()\n"
  },
  {
    "path": "docs/examples/request.js",
    "content": "'use strict'\n\nconst { request } = require('../../')\n\nasync function getRequest (port = 3001) {\n  // A simple GET request\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(`http://localhost:${port}/`)\n\n  const data = await body.text()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', data)\n}\n\nasync function postJSONRequest (port = 3001) {\n  // Make a JSON POST request:\n\n  const requestBody = {\n    hello: 'JSON POST Example body'\n  }\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/json`,\n    { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(requestBody) }\n  )\n\n  // .json() will fail if we did not receive a valid json body in response:\n  const decodedJson = await body.json()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', decodedJson)\n}\n\nasync function postFormRequest (port = 3001) {\n  // Make a URL-encoded form POST request:\n  const qs = require('node:querystring')\n\n  const requestBody = {\n    hello: 'URL Encoded Example body'\n  }\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/form`,\n    { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: qs.stringify(requestBody) }\n  )\n\n  const data = await body.text()\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  console.log('data', data)\n}\n\nasync function deleteRequest (port = 3001) {\n  // Make a DELETE request\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(\n    `http://localhost:${port}/something`,\n    { method: 'DELETE' }\n  )\n\n  console.log('response received', statusCode)\n  console.log('headers', headers)\n  // For a DELETE request we expect a 204 response with no body if successful, in which case getting the body content with .json() will fail\n  if (statusCode === 204) {\n    console.log('delete successful')\n    // always consume the body if there is one:\n    await body.dump()\n  } else {\n    const data = await body.text()\n    console.log('received unexpected data', data)\n  }\n}\n\nmodule.exports = {\n  getRequest,\n  postJSONRequest,\n  postFormRequest,\n  deleteRequest\n}\n"
  },
  {
    "path": "docs/examples/snapshot-testing.js",
    "content": "const { SnapshotAgent, setGlobalDispatcher, getGlobalDispatcher, request } = require('../../index.js')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { tmpdir } = require('node:os')\nconst { join } = require('node:path')\n\n/**\n * Example: Basic Snapshot Testing\n *\n * This example demonstrates how to use SnapshotAgent to record API\n * interactions and replay them in tests for consistent, offline testing.\n */\n\nasync function basicSnapshotExample () {\n  console.log('🚀 Basic Snapshot Testing Example\\n')\n\n  // Create a temporary snapshot file path\n  const snapshotPath = join(tmpdir(), `snapshot-example-${Date.now()}.json`)\n  console.log(`📁 Using temporary snapshot file: ${snapshotPath}\\n`)\n\n  // Create a local test server\n  const server = createServer((req, res) => {\n    res.writeHead(200, { 'Content-Type': 'application/json' })\n    res.end(JSON.stringify({\n      message: 'Hello from test server!',\n      timestamp: new Date().toISOString(),\n      path: req.url\n    }))\n  })\n\n  await promisify(server.listen.bind(server))(0)\n  const { port } = server.address()\n  const origin = `http://localhost:${port}`\n\n  try {\n    // Step 1: Record mode - capture API responses\n    console.log('📹 Step 1: Recording API response...')\n\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setGlobalDispatcher(recordingAgent)\n\n    try {\n      // Make an API call that will be recorded\n      const response = await request(`${origin}/api/test`)\n      const data = await response.body.json()\n\n      console.log(`✅ Recorded response: ${data.message}`)\n\n      // Save the recorded snapshots\n      await recordingAgent.saveSnapshots()\n      console.log('💾 Snapshot saved to temporary file\\n')\n    } finally {\n      setGlobalDispatcher(originalDispatcher)\n      recordingAgent.close()\n    }\n\n    // Step 2: Playback mode - use recorded responses (server can be down)\n    console.log('🎬 Step 2: Playing back recorded response...')\n    server.close() // Close server to prove we're using snapshots\n\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath\n    })\n\n    setGlobalDispatcher(playbackAgent)\n\n    try {\n      // This will use the recorded response instead of making a real request\n      const response = await request(`${origin}/api/test`)\n      const data = await response.body.json()\n\n      console.log(`✅ Playback response: ${data.message}`)\n      console.log('🎉 Successfully used recorded data instead of live server!')\n    } finally {\n      setGlobalDispatcher(originalDispatcher)\n      playbackAgent.close()\n    }\n  } finally {\n    // Ensure server is closed\n    if (server.listening) {\n      server.close()\n    }\n\n    // Clean up temporary file\n    try {\n      const { unlink } = require('node:fs/promises')\n      await unlink(snapshotPath)\n      console.log('\\n🗑️  Cleaned up temporary snapshot file')\n    } catch {\n      // File might not exist or already be deleted\n    }\n  }\n}\n\n// Main execution\nasync function main () {\n  await basicSnapshotExample()\n}\n\n// Run if called directly\nif (require.main === module) {\n  main().catch(console.error)\n}\n"
  },
  {
    "path": "docs/examples/socks5-proxy.js",
    "content": "'use strict'\n\nconst { Socks5Agent, request, fetch } = require('undici')\n\n// Basic example demonstrating SOCKS5 proxy usage\nasync function basicSocks5Example () {\n  console.log('=== Basic SOCKS5 Proxy Example ===')\n\n  try {\n    // Create SOCKS5 proxy wrapper\n    const socks5Proxy = new Socks5Agent('socks5://localhost:1080')\n\n    // Make request through SOCKS5 proxy\n    const response = await request('http://httpbin.org/ip', {\n      dispatcher: socks5Proxy\n    })\n\n    console.log('Status:', response.statusCode)\n    const body = await response.body.json()\n    console.log('Response:', body)\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Example with authentication\nasync function authenticatedSocks5Example () {\n  console.log('\\n=== Authenticated SOCKS5 Proxy Example ===')\n\n  try {\n    // Using credentials in URL\n    const socks5Proxy = new Socks5Agent('socks5://username:password@localhost:1080')\n\n    // Alternative: using options\n    // const socks5Proxy = new Socks5Agent('socks5://localhost:1080', {\n    //   username: 'username',\n    //   password: 'password'\n    // })\n\n    const response = await request('http://httpbin.org/headers', {\n      dispatcher: socks5Proxy\n    })\n\n    console.log('Status:', response.statusCode)\n    const body = await response.body.json()\n    console.log('Headers seen by server:', body.headers)\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Example with fetch API\nasync function fetchWithSocks5Example () {\n  console.log('\\n=== Fetch with SOCKS5 Proxy Example ===')\n\n  try {\n    const socks5Proxy = new Socks5Agent('socks5://localhost:1080')\n\n    const response = await fetch('http://httpbin.org/json', {\n      dispatcher: socks5Proxy\n    })\n\n    console.log('Status:', response.status)\n    const data = await response.json()\n    console.log('JSON data:', data)\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Example with HTTPS\nasync function httpsWithSocks5Example () {\n  console.log('\\n=== HTTPS with SOCKS5 Proxy Example ===')\n\n  try {\n    const socks5Proxy = new Socks5Agent('socks5://localhost:1080')\n\n    const response = await request('https://httpbin.org/ip', {\n      dispatcher: socks5Proxy\n    })\n\n    console.log('Status:', response.statusCode)\n    const body = await response.body.json()\n    console.log('HTTPS Response:', body)\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Example with connection pooling\nasync function connectionPoolingExample () {\n  console.log('\\n=== Connection Pooling Example ===')\n\n  try {\n    const socks5Proxy = new Socks5Agent('socks5://localhost:1080', {\n      connections: 5,  // Allow up to 5 concurrent connections\n      pipelining: 1    // Enable HTTP/1.1 pipelining\n    })\n\n    // Make multiple concurrent requests\n    const requests = []\n    for (let i = 0; i < 3; i++) {\n      requests.push(\n        request(`http://httpbin.org/delay/${i}`, {\n          dispatcher: socks5Proxy\n        })\n      )\n    }\n\n    console.log('Making 3 concurrent requests...')\n    const responses = await Promise.all(requests)\n\n    for (let i = 0; i < responses.length; i++) {\n      console.log(`Request ${i + 1} status:`, responses[i].statusCode)\n      // Consume body to avoid warnings\n      await responses[i].body.dump()\n    }\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Example with error handling\nasync function errorHandlingExample () {\n  console.log('\\n=== Error Handling Example ===')\n\n  try {\n    // Intentionally use a non-existent proxy\n    const socks5Proxy = new Socks5Agent('socks5://localhost:9999')\n\n    await request('http://httpbin.org/ip', {\n      dispatcher: socks5Proxy\n    })\n  } catch (error) {\n    console.log('Caught expected error:', error.message)\n    console.log('Error code:', error.code)\n  }\n}\n\n// Global dispatcher example\nasync function globalDispatcherExample () {\n  console.log('\\n=== Global Dispatcher Example ===')\n\n  const { setGlobalDispatcher, getGlobalDispatcher } = require('undici')\n\n  try {\n    const socks5Proxy = new Socks5Agent('socks5://localhost:1080')\n\n    // Save original dispatcher\n    const originalDispatcher = getGlobalDispatcher()\n\n    // Set SOCKS5 proxy as global dispatcher\n    setGlobalDispatcher(socks5Proxy)\n\n    // All requests now go through SOCKS5 proxy automatically\n    const response = await request('http://httpbin.org/ip')\n\n    console.log('Status:', response.statusCode)\n    const body = await response.body.json()\n    console.log('Response through global SOCKS5 proxy:', body)\n\n    // Restore original dispatcher\n    setGlobalDispatcher(originalDispatcher)\n\n    await socks5Proxy.close()\n  } catch (error) {\n    console.error('Error:', error.message)\n  }\n}\n\n// Run examples\nasync function runExamples () {\n  console.log('SOCKS5 Proxy Examples for Undici')\n  console.log('================================')\n  console.log('Note: These examples require a SOCKS5 proxy running on localhost:1080')\n  console.log('You can use tools like dante-server, shadowsocks, or SSH tunneling.\\n')\n\n  await basicSocks5Example()\n  await authenticatedSocks5Example()\n  await fetchWithSocks5Example()\n  await httpsWithSocks5Example()\n  await connectionPoolingExample()\n  await errorHandlingExample()\n  await globalDispatcherExample()\n\n  console.log('\\n=== All examples completed ===')\n}\n\n// Only run if this file is executed directly\nif (require.main === module) {\n  runExamples().catch(console.error)\n}\n\nmodule.exports = {\n  basicSocks5Example,\n  authenticatedSocks5Example,\n  fetchWithSocks5Example,\n  httpsWithSocks5Example,\n  connectionPoolingExample,\n  errorHandlingExample,\n  globalDispatcherExample\n}\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Node.js Undici</title>\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n  <meta name=\"description\" content=\"A HTTP/1.1 client, written from scratch for Node.js.\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, minimum-scale=1.0\">\n  <link rel=\"stylesheet\" href=\"//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css\">\n  <link\n  rel=\"stylesheet\"\n  href=\"//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css\"\n  title=\"docsify-darklight-theme\"\n  type=\"text/css\"\n/>\n  <link rel=\"icon\" type=\"image/png\" href=\"https://nodejs.org/static/images/favicons/favicon.png\" />\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script>\n    window.$docsify = {\n      name: 'Node.js Undici',\n      repo: 'https://github.com/nodejs/undici',\n      loadSidebar: 'docsify/sidebar.md',\n      search: {\n        noData: {\n          '/': 'No results!'\n        },\n        paths: ['docs/'],\n        placeholder: {\n          '/': 'Search'\n        }\n      },\n      auto2top: true,\n      subMaxLevel: 3,\n      maxLevel: 3,\n      themeColor: '#2B91F0',\n      noCompileLinks: [\n        'benchmarks/.*'\n      ],\n      copyCode: {\n        buttonText: 'Copy Code',\n        errorText: 'Error',\n        successText: 'Copied',\n      },\n      relativePath: true,\n      markdown: {\n        renderer: {\n          // Mimic markedjs/marked behavior just modify href - https://github.com/markedjs/marked/blob/master/src/Renderer.ts#L178-L191\n          link(href, title, text) {\n            const originalHref = href;\n\n            if (href.startsWith('./')) {\n              // Use absolute path (e.g. ./docs/api.md => /docs/api.md) if href starts with ./ (e.g. ./api.md)\n              href = href.slice(1);\n            }\n\n            // Check for /docs/docs/ in the href and remove duplication it if present\n            href = href.startsWith('/docs/docs/') ? href.replace('/docs/', '/') : href;\n\n            // Check for /docs/ in the href and remove it if present\n            if (href.startsWith('/docs/')) {\n              // ignore paths /docs/api/ and /docs/best-practices/ in /docs/docs directory\n              if (!/^(\\/docs\\/(?:api|best-practices))(?:\\/|$)/.test(href)) {\n                href = href.includes('/docs/') ? href.replace('/docs/', '/') : href;\n              }\n            }\n\n            let target = '';\n            if (originalHref.startsWith('http')) {\n              // External link - default behavior is to open in a new window\n              return `<a href=\"${originalHref}\" ${title}\" target=\"_blank\" rel=\"noopener noreferrer\">${text}</a>`;\n            }\n\n            title = title ? `title=\"${title}\"` : '';\n            let out = `<a href=\"#${href}\" ${title}\">${text}</a>`;\n\n            return out;\n          },\n        },\n      },\n    }\n  </script>\n  <!-- Docsify v4 -->\n  <script src=\"//cdn.jsdelivr.net/npm/docsify@4\"></script>\n  <script src=\"//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js\"></script>\n  <script src=\"https://unpkg.com/docsify-copy-code@3\"></script>\n  <script\n  src=\"//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js\"\n  type=\"text/javascript\"\n></script>\n  <script type=\"module\">\n    import mermaid from \"//cdn.jsdelivr.net/npm/mermaid@10.8.0/+esm\";\n    mermaid.initialize({ startOnLoad: true });\n    window.mermaid = mermaid;\n  </script>\n  <script>\n    const plugin = (config) => (hook) => {\n      hook.afterEach((html, next) => {\n        const container = document.createElement('div');\n        container.innerHTML = html;\n\n        const elements = container.querySelectorAll('pre[data-lang=mermaid]')\n        for (const element of elements) {\n          const replacement = document.createElement('div');\n          replacement.textContent = element.textContent;\n          replacement.classList.add('mermaid');\n          element.parentNode.replaceChild(replacement, element);\n        }\n\n        next(container.innerHTML);\n      });\n\n      hook.doneEach(() => mermaid.run(config));\n    }\n\n    window.$docsify = window.$docsify || {};\n\n    window.$docsify.plugins = window.$docsify.plugins || []\n    window.$docsify.plugins.push(plugin(window.$docsify.mermaidConfig || { querySelector: \".mermaid\" }));\n  </script>\n</body>\n\n</html>"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"@undici/documentation\",\n  \"description\": \"Documentation site for the `undici` package.\",\n  \"scripts\": {\n    \"serve\": \"docsify serve .\"\n  },\n  \"dependencies\": {\n    \"docsify-cli\": \"^4.4.4\"\n  }\n}\n"
  },
  {
    "path": "eslint.config.js",
    "content": "'use strict'\n\nconst neo = require('neostandard')\nconst { installedExports } = require('./lib/global')\n\nmodule.exports = [\n  ...neo({\n    ignores: [\n      'lib/llhttp',\n      'test/fixtures/cache-tests',\n      'undici-fetch.js',\n      'test/web-platform-tests/wpt'\n    ],\n    noJsx: true,\n    ts: true\n  }),\n  {\n    rules: {\n      'n/prefer-node-protocol': ['error'],\n      'n/no-process-exit': 'error',\n      '@stylistic/comma-dangle': ['error', {\n        arrays: 'never',\n        objects: 'never',\n        imports: 'never',\n        exports: 'never',\n        functions: 'never'\n      }],\n      '@typescript-eslint/no-redeclare': 'off',\n      'no-restricted-globals': ['error',\n        ...installedExports.map(name => {\n          return {\n            name,\n            message: `Use undici-own ${name} instead of the global.`\n          }\n        })\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "index-fetch.js",
    "content": "'use strict'\n\nconst { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')\nconst EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')\nconst fetchImpl = require('./lib/web/fetch').fetch\n\n// Capture __filename at module load time for stack trace augmentation.\n// This may be undefined when bundled in environments like Node.js internals.\nconst currentFilename = typeof __filename !== 'undefined' ? __filename : undefined\n\nfunction appendFetchStackTrace (err, filename) {\n  if (!err || typeof err !== 'object') {\n    return\n  }\n\n  const stack = typeof err.stack === 'string' ? err.stack : ''\n  const normalizedFilename = filename.replace(/\\\\/g, '/')\n\n  if (stack && (stack.includes(filename) || stack.includes(normalizedFilename))) {\n    return\n  }\n\n  const capture = {}\n  Error.captureStackTrace(capture, appendFetchStackTrace)\n\n  if (!capture.stack) {\n    return\n  }\n\n  const captureLines = capture.stack.split('\\n').slice(1).join('\\n')\n\n  err.stack = stack ? `${stack}\\n${captureLines}` : capture.stack\n}\n\nmodule.exports.fetch = function fetch (init, options = undefined) {\n  return fetchImpl(init, options).catch(err => {\n    if (currentFilename) {\n      appendFetchStackTrace(err, currentFilename)\n    } else if (err && typeof err === 'object') {\n      Error.captureStackTrace(err, module.exports.fetch)\n    }\n    throw err\n  })\n}\nmodule.exports.FormData = require('./lib/web/fetch/formdata').FormData\nmodule.exports.Headers = require('./lib/web/fetch/headers').Headers\nmodule.exports.Response = require('./lib/web/fetch/response').Response\nmodule.exports.Request = require('./lib/web/fetch/request').Request\n\nconst { CloseEvent, ErrorEvent, MessageEvent, createFastMessageEvent } = require('./lib/web/websocket/events')\nmodule.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket\nmodule.exports.CloseEvent = CloseEvent\nmodule.exports.ErrorEvent = ErrorEvent\nmodule.exports.MessageEvent = MessageEvent\nmodule.exports.createFastMessageEvent = createFastMessageEvent\n\nmodule.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource\n\nconst api = require('./lib/api')\nconst Dispatcher = require('./lib/dispatcher/dispatcher')\nObject.assign(Dispatcher.prototype, api)\n// Expose the fetch implementation to be enabled in Node.js core via a flag\nmodule.exports.EnvHttpProxyAgent = EnvHttpProxyAgent\nmodule.exports.getGlobalDispatcher = getGlobalDispatcher\nmodule.exports.setGlobalDispatcher = setGlobalDispatcher\n"
  },
  {
    "path": "index.d.ts",
    "content": "import Undici from './types/index'\nexport default Undici\nexport * from './types/index'\n"
  },
  {
    "path": "index.js",
    "content": "'use strict'\n\nconst Client = require('./lib/dispatcher/client')\nconst Dispatcher = require('./lib/dispatcher/dispatcher')\nconst Pool = require('./lib/dispatcher/pool')\nconst BalancedPool = require('./lib/dispatcher/balanced-pool')\nconst RoundRobinPool = require('./lib/dispatcher/round-robin-pool')\nconst Agent = require('./lib/dispatcher/agent')\nconst ProxyAgent = require('./lib/dispatcher/proxy-agent')\nconst Socks5ProxyAgent = require('./lib/dispatcher/socks5-proxy-agent')\nconst EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')\nconst RetryAgent = require('./lib/dispatcher/retry-agent')\nconst H2CClient = require('./lib/dispatcher/h2c-client')\nconst errors = require('./lib/core/errors')\nconst util = require('./lib/core/util')\nconst { InvalidArgumentError } = errors\nconst api = require('./lib/api')\nconst buildConnector = require('./lib/core/connect')\nconst MockClient = require('./lib/mock/mock-client')\nconst { MockCallHistory, MockCallHistoryLog } = require('./lib/mock/mock-call-history')\nconst MockAgent = require('./lib/mock/mock-agent')\nconst MockPool = require('./lib/mock/mock-pool')\nconst SnapshotAgent = require('./lib/mock/snapshot-agent')\nconst mockErrors = require('./lib/mock/mock-errors')\nconst RetryHandler = require('./lib/handler/retry-handler')\nconst { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')\nconst DecoratorHandler = require('./lib/handler/decorator-handler')\nconst RedirectHandler = require('./lib/handler/redirect-handler')\n\nObject.assign(Dispatcher.prototype, api)\n\nmodule.exports.Dispatcher = Dispatcher\nmodule.exports.Client = Client\nmodule.exports.Pool = Pool\nmodule.exports.BalancedPool = BalancedPool\nmodule.exports.RoundRobinPool = RoundRobinPool\nmodule.exports.Agent = Agent\nmodule.exports.ProxyAgent = ProxyAgent\nmodule.exports.Socks5ProxyAgent = Socks5ProxyAgent\nmodule.exports.EnvHttpProxyAgent = EnvHttpProxyAgent\nmodule.exports.RetryAgent = RetryAgent\nmodule.exports.H2CClient = H2CClient\nmodule.exports.RetryHandler = RetryHandler\n\nmodule.exports.DecoratorHandler = DecoratorHandler\nmodule.exports.RedirectHandler = RedirectHandler\nmodule.exports.interceptors = {\n  redirect: require('./lib/interceptor/redirect'),\n  responseError: require('./lib/interceptor/response-error'),\n  retry: require('./lib/interceptor/retry'),\n  dump: require('./lib/interceptor/dump'),\n  dns: require('./lib/interceptor/dns'),\n  cache: require('./lib/interceptor/cache'),\n  decompress: require('./lib/interceptor/decompress'),\n  deduplicate: require('./lib/interceptor/deduplicate')\n}\n\nmodule.exports.cacheStores = {\n  MemoryCacheStore: require('./lib/cache/memory-cache-store')\n}\n\nconst SqliteCacheStore = require('./lib/cache/sqlite-cache-store')\nmodule.exports.cacheStores.SqliteCacheStore = SqliteCacheStore\n\nmodule.exports.buildConnector = buildConnector\nmodule.exports.errors = errors\nmodule.exports.util = {\n  parseHeaders: util.parseHeaders,\n  headerNameToString: util.headerNameToString\n}\n\nfunction makeDispatcher (fn) {\n  return (url, opts, handler) => {\n    if (typeof opts === 'function') {\n      handler = opts\n      opts = null\n    }\n\n    if (!url || (typeof url !== 'string' && typeof url !== 'object' && !(url instanceof URL))) {\n      throw new InvalidArgumentError('invalid url')\n    }\n\n    if (opts != null && typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    if (opts && opts.path != null) {\n      if (typeof opts.path !== 'string') {\n        throw new InvalidArgumentError('invalid opts.path')\n      }\n\n      let path = opts.path\n      if (!opts.path.startsWith('/')) {\n        path = `/${path}`\n      }\n\n      url = new URL(util.parseOrigin(url).origin + path)\n    } else {\n      if (!opts) {\n        opts = typeof url === 'object' ? url : {}\n      }\n\n      url = util.parseURL(url)\n    }\n\n    const { agent, dispatcher = getGlobalDispatcher() } = opts\n\n    if (agent) {\n      throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')\n    }\n\n    return fn.call(dispatcher, {\n      ...opts,\n      origin: url.origin,\n      path: url.search ? `${url.pathname}${url.search}` : url.pathname,\n      method: opts.method || (opts.body ? 'PUT' : 'GET')\n    }, handler)\n  }\n}\n\nmodule.exports.setGlobalDispatcher = setGlobalDispatcher\nmodule.exports.getGlobalDispatcher = getGlobalDispatcher\n\nconst fetchImpl = require('./lib/web/fetch').fetch\n\n// Capture __filename at module load time for stack trace augmentation.\n// This may be undefined when bundled in environments like Node.js internals.\nconst currentFilename = typeof __filename !== 'undefined' ? __filename : undefined\n\nfunction appendFetchStackTrace (err, filename) {\n  if (!err || typeof err !== 'object') {\n    return\n  }\n\n  const stack = typeof err.stack === 'string' ? err.stack : ''\n  const normalizedFilename = filename.replace(/\\\\/g, '/')\n\n  if (stack && (stack.includes(filename) || stack.includes(normalizedFilename))) {\n    return\n  }\n\n  const capture = {}\n  Error.captureStackTrace(capture, appendFetchStackTrace)\n\n  if (!capture.stack) {\n    return\n  }\n\n  const captureLines = capture.stack.split('\\n').slice(1).join('\\n')\n\n  err.stack = stack ? `${stack}\\n${captureLines}` : capture.stack\n}\n\nmodule.exports.fetch = function fetch (init, options = undefined) {\n  return fetchImpl(init, options).catch(err => {\n    if (currentFilename) {\n      appendFetchStackTrace(err, currentFilename)\n    } else if (err && typeof err === 'object') {\n      Error.captureStackTrace(err, module.exports.fetch)\n    }\n    throw err\n  })\n}\nmodule.exports.Headers = require('./lib/web/fetch/headers').Headers\nmodule.exports.Response = require('./lib/web/fetch/response').Response\nmodule.exports.Request = require('./lib/web/fetch/request').Request\nmodule.exports.FormData = require('./lib/web/fetch/formdata').FormData\n\nconst { setGlobalOrigin, getGlobalOrigin } = require('./lib/web/fetch/global')\n\nmodule.exports.setGlobalOrigin = setGlobalOrigin\nmodule.exports.getGlobalOrigin = getGlobalOrigin\n\nconst { CacheStorage } = require('./lib/web/cache/cachestorage')\nconst { kConstruct } = require('./lib/core/symbols')\n\nmodule.exports.caches = new CacheStorage(kConstruct)\n\nconst { deleteCookie, getCookies, getSetCookies, setCookie, parseCookie } = require('./lib/web/cookies')\n\nmodule.exports.deleteCookie = deleteCookie\nmodule.exports.getCookies = getCookies\nmodule.exports.getSetCookies = getSetCookies\nmodule.exports.setCookie = setCookie\nmodule.exports.parseCookie = parseCookie\n\nconst { parseMIMEType, serializeAMimeType } = require('./lib/web/fetch/data-url')\n\nmodule.exports.parseMIMEType = parseMIMEType\nmodule.exports.serializeAMimeType = serializeAMimeType\n\nconst { CloseEvent, ErrorEvent, MessageEvent } = require('./lib/web/websocket/events')\nconst { WebSocket, ping } = require('./lib/web/websocket/websocket')\nmodule.exports.WebSocket = WebSocket\nmodule.exports.CloseEvent = CloseEvent\nmodule.exports.ErrorEvent = ErrorEvent\nmodule.exports.MessageEvent = MessageEvent\nmodule.exports.ping = ping\n\nmodule.exports.WebSocketStream = require('./lib/web/websocket/stream/websocketstream').WebSocketStream\nmodule.exports.WebSocketError = require('./lib/web/websocket/stream/websocketerror').WebSocketError\n\nmodule.exports.request = makeDispatcher(api.request)\nmodule.exports.stream = makeDispatcher(api.stream)\nmodule.exports.pipeline = makeDispatcher(api.pipeline)\nmodule.exports.connect = makeDispatcher(api.connect)\nmodule.exports.upgrade = makeDispatcher(api.upgrade)\n\nmodule.exports.MockClient = MockClient\nmodule.exports.MockCallHistory = MockCallHistory\nmodule.exports.MockCallHistoryLog = MockCallHistoryLog\nmodule.exports.MockPool = MockPool\nmodule.exports.MockAgent = MockAgent\nmodule.exports.SnapshotAgent = SnapshotAgent\nmodule.exports.mockErrors = mockErrors\n\nconst { EventSource } = require('./lib/web/eventsource/eventsource')\n\nmodule.exports.EventSource = EventSource\n\nfunction install () {\n  globalThis.fetch = module.exports.fetch\n  globalThis.Headers = module.exports.Headers\n  globalThis.Response = module.exports.Response\n  globalThis.Request = module.exports.Request\n  globalThis.FormData = module.exports.FormData\n  globalThis.WebSocket = module.exports.WebSocket\n  globalThis.CloseEvent = module.exports.CloseEvent\n  globalThis.ErrorEvent = module.exports.ErrorEvent\n  globalThis.MessageEvent = module.exports.MessageEvent\n  globalThis.EventSource = module.exports.EventSource\n}\n\nmodule.exports.install = install\n"
  },
  {
    "path": "lib/api/abort-signal.js",
    "content": "'use strict'\n\nconst { addAbortListener } = require('../core/util')\nconst { RequestAbortedError } = require('../core/errors')\n\nconst kListener = Symbol('kListener')\nconst kSignal = Symbol('kSignal')\n\nfunction abort (self) {\n  if (self.abort) {\n    self.abort(self[kSignal]?.reason)\n  } else {\n    self.reason = self[kSignal]?.reason ?? new RequestAbortedError()\n  }\n  removeSignal(self)\n}\n\nfunction addSignal (self, signal) {\n  self.reason = null\n\n  self[kSignal] = null\n  self[kListener] = null\n\n  if (!signal) {\n    return\n  }\n\n  if (signal.aborted) {\n    abort(self)\n    return\n  }\n\n  self[kSignal] = signal\n  self[kListener] = () => {\n    abort(self)\n  }\n\n  addAbortListener(self[kSignal], self[kListener])\n}\n\nfunction removeSignal (self) {\n  if (!self[kSignal]) {\n    return\n  }\n\n  if ('removeEventListener' in self[kSignal]) {\n    self[kSignal].removeEventListener('abort', self[kListener])\n  } else {\n    self[kSignal].removeListener('abort', self[kListener])\n  }\n\n  self[kSignal] = null\n  self[kListener] = null\n}\n\nmodule.exports = {\n  addSignal,\n  removeSignal\n}\n"
  },
  {
    "path": "lib/api/api-connect.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { AsyncResource } = require('node:async_hooks')\nconst { InvalidArgumentError, SocketError } = require('../core/errors')\nconst util = require('../core/util')\nconst { addSignal, removeSignal } = require('./abort-signal')\n\nclass ConnectHandler extends AsyncResource {\n  constructor (opts, callback) {\n    if (!opts || typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    if (typeof callback !== 'function') {\n      throw new InvalidArgumentError('invalid callback')\n    }\n\n    const { signal, opaque, responseHeaders } = opts\n\n    if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {\n      throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')\n    }\n\n    super('UNDICI_CONNECT')\n\n    this.opaque = opaque || null\n    this.responseHeaders = responseHeaders || null\n    this.callback = callback\n    this.abort = null\n\n    addSignal(this, signal)\n  }\n\n  onConnect (abort, context) {\n    if (this.reason) {\n      abort(this.reason)\n      return\n    }\n\n    assert(this.callback)\n\n    this.abort = abort\n    this.context = context\n  }\n\n  onHeaders () {\n    throw new SocketError('bad connect', null)\n  }\n\n  onUpgrade (statusCode, rawHeaders, socket) {\n    const { callback, opaque, context } = this\n\n    removeSignal(this)\n\n    this.callback = null\n\n    let headers = rawHeaders\n    // Indicates is an HTTP2Session\n    if (headers != null) {\n      headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n    }\n\n    this.runInAsyncScope(callback, null, null, {\n      statusCode,\n      headers,\n      socket,\n      opaque,\n      context\n    })\n  }\n\n  onError (err) {\n    const { callback, opaque } = this\n\n    removeSignal(this)\n\n    if (callback) {\n      this.callback = null\n      queueMicrotask(() => {\n        this.runInAsyncScope(callback, null, err, { opaque })\n      })\n    }\n  }\n}\n\nfunction connect (opts, callback) {\n  if (callback === undefined) {\n    return new Promise((resolve, reject) => {\n      connect.call(this, opts, (err, data) => {\n        return err ? reject(err) : resolve(data)\n      })\n    })\n  }\n\n  try {\n    const connectHandler = new ConnectHandler(opts, callback)\n    const connectOptions = { ...opts, method: 'CONNECT' }\n\n    this.dispatch(connectOptions, connectHandler)\n  } catch (err) {\n    if (typeof callback !== 'function') {\n      throw err\n    }\n    const opaque = opts?.opaque\n    queueMicrotask(() => callback(err, { opaque }))\n  }\n}\n\nmodule.exports = connect\n"
  },
  {
    "path": "lib/api/api-pipeline.js",
    "content": "'use strict'\n\nconst {\n  Readable,\n  Duplex,\n  PassThrough\n} = require('node:stream')\nconst assert = require('node:assert')\nconst { AsyncResource } = require('node:async_hooks')\nconst {\n  InvalidArgumentError,\n  InvalidReturnValueError,\n  RequestAbortedError\n} = require('../core/errors')\nconst util = require('../core/util')\nconst { addSignal, removeSignal } = require('./abort-signal')\n\nfunction noop () {}\n\nconst kResume = Symbol('resume')\n\nclass PipelineRequest extends Readable {\n  constructor () {\n    super({ autoDestroy: true })\n\n    this[kResume] = null\n  }\n\n  _read () {\n    const { [kResume]: resume } = this\n\n    if (resume) {\n      this[kResume] = null\n      resume()\n    }\n  }\n\n  _destroy (err, callback) {\n    this._read()\n\n    callback(err)\n  }\n}\n\nclass PipelineResponse extends Readable {\n  constructor (resume) {\n    super({ autoDestroy: true })\n    this[kResume] = resume\n  }\n\n  _read () {\n    this[kResume]()\n  }\n\n  _destroy (err, callback) {\n    if (!err && !this._readableState.endEmitted) {\n      err = new RequestAbortedError()\n    }\n\n    callback(err)\n  }\n}\n\nclass PipelineHandler extends AsyncResource {\n  constructor (opts, handler) {\n    if (!opts || typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    if (typeof handler !== 'function') {\n      throw new InvalidArgumentError('invalid handler')\n    }\n\n    const { signal, method, opaque, onInfo, responseHeaders } = opts\n\n    if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {\n      throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')\n    }\n\n    if (method === 'CONNECT') {\n      throw new InvalidArgumentError('invalid method')\n    }\n\n    if (onInfo && typeof onInfo !== 'function') {\n      throw new InvalidArgumentError('invalid onInfo callback')\n    }\n\n    super('UNDICI_PIPELINE')\n\n    this.opaque = opaque || null\n    this.responseHeaders = responseHeaders || null\n    this.handler = handler\n    this.abort = null\n    this.context = null\n    this.onInfo = onInfo || null\n\n    this.req = new PipelineRequest().on('error', noop)\n\n    this.ret = new Duplex({\n      readableObjectMode: opts.objectMode,\n      autoDestroy: true,\n      read: () => {\n        const { body } = this\n\n        if (body?.resume) {\n          body.resume()\n        }\n      },\n      write: (chunk, encoding, callback) => {\n        const { req } = this\n\n        if (req.push(chunk, encoding) || req._readableState.destroyed) {\n          callback()\n        } else {\n          req[kResume] = callback\n        }\n      },\n      destroy: (err, callback) => {\n        const { body, req, res, ret, abort } = this\n\n        if (!err && !ret._readableState.endEmitted) {\n          err = new RequestAbortedError()\n        }\n\n        if (abort && err) {\n          abort()\n        }\n\n        util.destroy(body, err)\n        util.destroy(req, err)\n        util.destroy(res, err)\n\n        removeSignal(this)\n\n        callback(err)\n      }\n    }).on('prefinish', () => {\n      const { req } = this\n\n      // Node < 15 does not call _final in same tick.\n      req.push(null)\n    })\n\n    this.res = null\n\n    addSignal(this, signal)\n  }\n\n  onConnect (abort, context) {\n    const { res } = this\n\n    if (this.reason) {\n      abort(this.reason)\n      return\n    }\n\n    assert(!res, 'pipeline cannot be retried')\n\n    this.abort = abort\n    this.context = context\n  }\n\n  onHeaders (statusCode, rawHeaders, resume) {\n    const { opaque, handler, context } = this\n\n    if (statusCode < 200) {\n      if (this.onInfo) {\n        const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n        this.onInfo({ statusCode, headers })\n      }\n      return\n    }\n\n    this.res = new PipelineResponse(resume)\n\n    let body\n    try {\n      this.handler = null\n      const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n      body = this.runInAsyncScope(handler, null, {\n        statusCode,\n        headers,\n        opaque,\n        body: this.res,\n        context\n      })\n    } catch (err) {\n      this.res.on('error', noop)\n      throw err\n    }\n\n    if (!body || typeof body.on !== 'function') {\n      throw new InvalidReturnValueError('expected Readable')\n    }\n\n    body\n      .on('data', (chunk) => {\n        const { ret, body } = this\n\n        if (!ret.push(chunk) && body.pause) {\n          body.pause()\n        }\n      })\n      .on('error', (err) => {\n        const { ret } = this\n\n        util.destroy(ret, err)\n      })\n      .on('end', () => {\n        const { ret } = this\n\n        ret.push(null)\n      })\n      .on('close', () => {\n        const { ret } = this\n\n        if (!ret._readableState.ended) {\n          util.destroy(ret, new RequestAbortedError())\n        }\n      })\n\n    this.body = body\n  }\n\n  onData (chunk) {\n    const { res } = this\n    return res.push(chunk)\n  }\n\n  onComplete (trailers) {\n    const { res } = this\n    res.push(null)\n  }\n\n  onError (err) {\n    const { ret } = this\n    this.handler = null\n    util.destroy(ret, err)\n  }\n}\n\nfunction pipeline (opts, handler) {\n  try {\n    const pipelineHandler = new PipelineHandler(opts, handler)\n    this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler)\n    return pipelineHandler.ret\n  } catch (err) {\n    return new PassThrough().destroy(err)\n  }\n}\n\nmodule.exports = pipeline\n"
  },
  {
    "path": "lib/api/api-request.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { AsyncResource } = require('node:async_hooks')\nconst { Readable } = require('./readable')\nconst { InvalidArgumentError, RequestAbortedError } = require('../core/errors')\nconst util = require('../core/util')\n\nfunction noop () {}\n\nclass RequestHandler extends AsyncResource {\n  constructor (opts, callback) {\n    if (!opts || typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    const { signal, method, opaque, body, onInfo, responseHeaders, highWaterMark } = opts\n\n    try {\n      if (typeof callback !== 'function') {\n        throw new InvalidArgumentError('invalid callback')\n      }\n\n      if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {\n        throw new InvalidArgumentError('invalid highWaterMark')\n      }\n\n      if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {\n        throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')\n      }\n\n      if (method === 'CONNECT') {\n        throw new InvalidArgumentError('invalid method')\n      }\n\n      if (onInfo && typeof onInfo !== 'function') {\n        throw new InvalidArgumentError('invalid onInfo callback')\n      }\n\n      super('UNDICI_REQUEST')\n    } catch (err) {\n      if (util.isStream(body)) {\n        util.destroy(body.on('error', noop), err)\n      }\n      throw err\n    }\n\n    this.method = method\n    this.responseHeaders = responseHeaders || null\n    this.opaque = opaque || null\n    this.callback = callback\n    this.res = null\n    this.abort = null\n    this.body = body\n    this.trailers = {}\n    this.context = null\n    this.onInfo = onInfo || null\n    this.highWaterMark = highWaterMark\n    this.reason = null\n    this.removeAbortListener = null\n\n    if (signal?.aborted) {\n      this.reason = signal.reason ?? new RequestAbortedError()\n    } else if (signal) {\n      this.removeAbortListener = util.addAbortListener(signal, () => {\n        this.reason = signal.reason ?? new RequestAbortedError()\n        if (this.res) {\n          util.destroy(this.res.on('error', noop), this.reason)\n        } else if (this.abort) {\n          this.abort(this.reason)\n        }\n      })\n    }\n  }\n\n  onConnect (abort, context) {\n    if (this.reason) {\n      abort(this.reason)\n      return\n    }\n\n    assert(this.callback)\n\n    this.abort = abort\n    this.context = context\n  }\n\n  onHeaders (statusCode, rawHeaders, resume, statusMessage) {\n    const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this\n\n    const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n\n    if (statusCode < 200) {\n      if (this.onInfo) {\n        this.onInfo({ statusCode, headers })\n      }\n      return\n    }\n\n    const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers\n    const contentType = parsedHeaders['content-type']\n    const contentLength = parsedHeaders['content-length']\n    const res = new Readable({\n      resume,\n      abort,\n      contentType,\n      contentLength: this.method !== 'HEAD' && contentLength\n        ? Number(contentLength)\n        : null,\n      highWaterMark\n    })\n\n    if (this.removeAbortListener) {\n      res.on('close', this.removeAbortListener)\n      this.removeAbortListener = null\n    }\n\n    this.callback = null\n    this.res = res\n    if (callback !== null) {\n      try {\n        this.runInAsyncScope(callback, null, null, {\n          statusCode,\n          statusText: statusMessage,\n          headers,\n          trailers: this.trailers,\n          opaque,\n          body: res,\n          context\n        })\n      } catch (err) {\n        // If the callback throws synchronously, we need to handle it\n        // Remove reference to res to allow res being garbage collected\n        this.res = null\n\n        // Destroy the response stream\n        util.destroy(res.on('error', noop), err)\n\n        // Use queueMicrotask to re-throw the error so it reaches uncaughtException\n        queueMicrotask(() => {\n          throw err\n        })\n      }\n    }\n  }\n\n  onData (chunk) {\n    return this.res.push(chunk)\n  }\n\n  onComplete (trailers) {\n    util.parseHeaders(trailers, this.trailers)\n    this.res.push(null)\n  }\n\n  onError (err) {\n    const { res, callback, body, opaque } = this\n\n    if (callback) {\n      // TODO: Does this need queueMicrotask?\n      this.callback = null\n      queueMicrotask(() => {\n        this.runInAsyncScope(callback, null, err, { opaque })\n      })\n    }\n\n    if (res) {\n      this.res = null\n      // Ensure all queued handlers are invoked before destroying res.\n      queueMicrotask(() => {\n        util.destroy(res.on('error', noop), err)\n      })\n    }\n\n    if (body) {\n      this.body = null\n\n      if (util.isStream(body)) {\n        body.on('error', noop)\n        util.destroy(body, err)\n      }\n    }\n\n    if (this.removeAbortListener) {\n      this.removeAbortListener()\n      this.removeAbortListener = null\n    }\n  }\n}\n\nfunction request (opts, callback) {\n  if (callback === undefined) {\n    return new Promise((resolve, reject) => {\n      request.call(this, opts, (err, data) => {\n        return err ? reject(err) : resolve(data)\n      })\n    })\n  }\n\n  try {\n    const handler = new RequestHandler(opts, callback)\n\n    this.dispatch(opts, handler)\n  } catch (err) {\n    if (typeof callback !== 'function') {\n      throw err\n    }\n    const opaque = opts?.opaque\n    queueMicrotask(() => callback(err, { opaque }))\n  }\n}\n\nmodule.exports = request\nmodule.exports.RequestHandler = RequestHandler\n"
  },
  {
    "path": "lib/api/api-stream.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { finished } = require('node:stream')\nconst { AsyncResource } = require('node:async_hooks')\nconst { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')\nconst util = require('../core/util')\nconst { addSignal, removeSignal } = require('./abort-signal')\n\nfunction noop () {}\n\nclass StreamHandler extends AsyncResource {\n  constructor (opts, factory, callback) {\n    if (!opts || typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    const { signal, method, opaque, body, onInfo, responseHeaders } = opts\n\n    try {\n      if (typeof callback !== 'function') {\n        throw new InvalidArgumentError('invalid callback')\n      }\n\n      if (typeof factory !== 'function') {\n        throw new InvalidArgumentError('invalid factory')\n      }\n\n      if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {\n        throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')\n      }\n\n      if (method === 'CONNECT') {\n        throw new InvalidArgumentError('invalid method')\n      }\n\n      if (onInfo && typeof onInfo !== 'function') {\n        throw new InvalidArgumentError('invalid onInfo callback')\n      }\n\n      super('UNDICI_STREAM')\n    } catch (err) {\n      if (util.isStream(body)) {\n        util.destroy(body.on('error', noop), err)\n      }\n      throw err\n    }\n\n    this.responseHeaders = responseHeaders || null\n    this.opaque = opaque || null\n    this.factory = factory\n    this.callback = callback\n    this.res = null\n    this.abort = null\n    this.context = null\n    this.trailers = null\n    this.body = body\n    this.onInfo = onInfo || null\n\n    if (util.isStream(body)) {\n      body.on('error', (err) => {\n        this.onError(err)\n      })\n    }\n\n    addSignal(this, signal)\n  }\n\n  onConnect (abort, context) {\n    if (this.reason) {\n      abort(this.reason)\n      return\n    }\n\n    assert(this.callback)\n\n    this.abort = abort\n    this.context = context\n  }\n\n  onHeaders (statusCode, rawHeaders, resume, statusMessage) {\n    const { factory, opaque, context, responseHeaders } = this\n\n    const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n\n    if (statusCode < 200) {\n      if (this.onInfo) {\n        this.onInfo({ statusCode, headers })\n      }\n      return\n    }\n\n    this.factory = null\n\n    if (factory === null) {\n      return\n    }\n\n    const res = this.runInAsyncScope(factory, null, {\n      statusCode,\n      headers,\n      opaque,\n      context\n    })\n\n    if (\n      !res ||\n      typeof res.write !== 'function' ||\n      typeof res.end !== 'function' ||\n      typeof res.on !== 'function'\n    ) {\n      throw new InvalidReturnValueError('expected Writable')\n    }\n\n    // TODO: Avoid finished. It registers an unnecessary amount of listeners.\n    finished(res, { readable: false }, (err) => {\n      const { callback, res, opaque, trailers, abort } = this\n\n      this.res = null\n      if (err || !res?.readable) {\n        util.destroy(res, err)\n      }\n\n      this.callback = null\n      this.runInAsyncScope(callback, null, err || null, { opaque, trailers })\n\n      if (err) {\n        abort()\n      }\n    })\n\n    res.on('drain', resume)\n\n    this.res = res\n\n    const needDrain = res.writableNeedDrain !== undefined\n      ? res.writableNeedDrain\n      : res._writableState?.needDrain\n\n    return needDrain !== true\n  }\n\n  onData (chunk) {\n    const { res } = this\n\n    return res ? res.write(chunk) : true\n  }\n\n  onComplete (trailers) {\n    const { res } = this\n\n    removeSignal(this)\n\n    if (!res) {\n      return\n    }\n\n    this.trailers = util.parseHeaders(trailers)\n\n    res.end()\n  }\n\n  onError (err) {\n    const { res, callback, opaque, body } = this\n\n    removeSignal(this)\n\n    this.factory = null\n\n    if (res) {\n      this.res = null\n      util.destroy(res, err)\n    } else if (callback) {\n      this.callback = null\n      queueMicrotask(() => {\n        this.runInAsyncScope(callback, null, err, { opaque })\n      })\n    }\n\n    if (body) {\n      this.body = null\n      util.destroy(body, err)\n    }\n  }\n}\n\nfunction stream (opts, factory, callback) {\n  if (callback === undefined) {\n    return new Promise((resolve, reject) => {\n      stream.call(this, opts, factory, (err, data) => {\n        return err ? reject(err) : resolve(data)\n      })\n    })\n  }\n\n  try {\n    const handler = new StreamHandler(opts, factory, callback)\n\n    this.dispatch(opts, handler)\n  } catch (err) {\n    if (typeof callback !== 'function') {\n      throw err\n    }\n    const opaque = opts?.opaque\n    queueMicrotask(() => callback(err, { opaque }))\n  }\n}\n\nmodule.exports = stream\n"
  },
  {
    "path": "lib/api/api-upgrade.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError, SocketError } = require('../core/errors')\nconst { AsyncResource } = require('node:async_hooks')\nconst assert = require('node:assert')\nconst util = require('../core/util')\nconst { kHTTP2Stream } = require('../core/symbols')\nconst { addSignal, removeSignal } = require('./abort-signal')\n\nclass UpgradeHandler extends AsyncResource {\n  constructor (opts, callback) {\n    if (!opts || typeof opts !== 'object') {\n      throw new InvalidArgumentError('invalid opts')\n    }\n\n    if (typeof callback !== 'function') {\n      throw new InvalidArgumentError('invalid callback')\n    }\n\n    const { signal, opaque, responseHeaders } = opts\n\n    if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {\n      throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')\n    }\n\n    super('UNDICI_UPGRADE')\n\n    this.responseHeaders = responseHeaders || null\n    this.opaque = opaque || null\n    this.callback = callback\n    this.abort = null\n    this.context = null\n\n    addSignal(this, signal)\n  }\n\n  onConnect (abort, context) {\n    if (this.reason) {\n      abort(this.reason)\n      return\n    }\n\n    assert(this.callback)\n\n    this.abort = abort\n    this.context = null\n  }\n\n  onHeaders () {\n    throw new SocketError('bad upgrade', null)\n  }\n\n  onUpgrade (statusCode, rawHeaders, socket) {\n    assert(socket[kHTTP2Stream] === true ? statusCode === 200 : statusCode === 101)\n\n    const { callback, opaque, context } = this\n\n    removeSignal(this)\n\n    this.callback = null\n    const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)\n    this.runInAsyncScope(callback, null, null, {\n      headers,\n      socket,\n      opaque,\n      context\n    })\n  }\n\n  onError (err) {\n    const { callback, opaque } = this\n\n    removeSignal(this)\n\n    if (callback) {\n      this.callback = null\n      queueMicrotask(() => {\n        this.runInAsyncScope(callback, null, err, { opaque })\n      })\n    }\n  }\n}\n\nfunction upgrade (opts, callback) {\n  if (callback === undefined) {\n    return new Promise((resolve, reject) => {\n      upgrade.call(this, opts, (err, data) => {\n        return err ? reject(err) : resolve(data)\n      })\n    })\n  }\n\n  try {\n    const upgradeHandler = new UpgradeHandler(opts, callback)\n    const upgradeOpts = {\n      ...opts,\n      method: opts.method || 'GET',\n      upgrade: opts.protocol || 'Websocket'\n    }\n\n    this.dispatch(upgradeOpts, upgradeHandler)\n  } catch (err) {\n    if (typeof callback !== 'function') {\n      throw err\n    }\n    const opaque = opts?.opaque\n    queueMicrotask(() => callback(err, { opaque }))\n  }\n}\n\nmodule.exports = upgrade\n"
  },
  {
    "path": "lib/api/index.js",
    "content": "'use strict'\n\nmodule.exports.request = require('./api-request')\nmodule.exports.stream = require('./api-stream')\nmodule.exports.pipeline = require('./api-pipeline')\nmodule.exports.upgrade = require('./api-upgrade')\nmodule.exports.connect = require('./api-connect')\n"
  },
  {
    "path": "lib/api/readable.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { Readable } = require('node:stream')\nconst { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require('../core/errors')\nconst util = require('../core/util')\nconst { ReadableStreamFrom } = require('../core/util')\n\nconst kConsume = Symbol('kConsume')\nconst kReading = Symbol('kReading')\nconst kBody = Symbol('kBody')\nconst kAbort = Symbol('kAbort')\nconst kContentType = Symbol('kContentType')\nconst kContentLength = Symbol('kContentLength')\nconst kUsed = Symbol('kUsed')\nconst kBytesRead = Symbol('kBytesRead')\n\nconst noop = () => {}\n\n/**\n * @class\n * @extends {Readable}\n * @see https://fetch.spec.whatwg.org/#body\n */\nclass BodyReadable extends Readable {\n  /**\n   * @param {object} opts\n   * @param {(this: Readable, size: number) => void} opts.resume\n   * @param {() => (void | null)} opts.abort\n   * @param {string} [opts.contentType = '']\n   * @param {number} [opts.contentLength]\n   * @param {number} [opts.highWaterMark = 64 * 1024]\n   */\n  constructor ({\n    resume,\n    abort,\n    contentType = '',\n    contentLength,\n    highWaterMark = 64 * 1024 // Same as nodejs fs streams.\n  }) {\n    super({\n      autoDestroy: true,\n      read: resume,\n      highWaterMark\n    })\n\n    this._readableState.dataEmitted = false\n\n    this[kAbort] = abort\n\n    /** @type {Consume | null} */\n    this[kConsume] = null\n\n    /** @type {number} */\n    this[kBytesRead] = 0\n\n    /** @type {ReadableStream|null} */\n    this[kBody] = null\n\n    /** @type {boolean} */\n    this[kUsed] = false\n\n    /** @type {string} */\n    this[kContentType] = contentType\n\n    /** @type {number|null} */\n    this[kContentLength] = Number.isFinite(contentLength) ? contentLength : null\n\n    /**\n     * Is stream being consumed through Readable API?\n     * This is an optimization so that we avoid checking\n     * for 'data' and 'readable' listeners in the hot path\n     * inside push().\n     *\n     * @type {boolean}\n     */\n    this[kReading] = false\n  }\n\n  /**\n   * @param {Error|null} err\n   * @param {(error:(Error|null)) => void} callback\n   * @returns {void}\n   */\n  _destroy (err, callback) {\n    if (!err && !this._readableState.endEmitted) {\n      err = new RequestAbortedError()\n    }\n\n    if (err) {\n      this[kAbort]()\n    }\n\n    // Workaround for Node \"bug\". If the stream is destroyed in same\n    // tick as it is created, then a user who is waiting for a\n    // promise (i.e micro tick) for installing an 'error' listener will\n    // never get a chance and will always encounter an unhandled exception.\n    if (!this[kUsed]) {\n      setImmediate(callback, err)\n    } else {\n      callback(err)\n    }\n  }\n\n  /**\n   * @param {string|symbol} event\n   * @param {(...args: any[]) => void} listener\n   * @returns {this}\n   */\n  on (event, listener) {\n    if (event === 'data' || event === 'readable') {\n      this[kReading] = true\n      this[kUsed] = true\n    }\n    return super.on(event, listener)\n  }\n\n  /**\n   * @param {string|symbol} event\n   * @param {(...args: any[]) => void} listener\n   * @returns {this}\n   */\n  addListener (event, listener) {\n    return this.on(event, listener)\n  }\n\n  /**\n   * @param {string|symbol} event\n   * @param {(...args: any[]) => void} listener\n   * @returns {this}\n   */\n  off (event, listener) {\n    const ret = super.off(event, listener)\n    if (event === 'data' || event === 'readable') {\n      this[kReading] = (\n        this.listenerCount('data') > 0 ||\n        this.listenerCount('readable') > 0\n      )\n    }\n    return ret\n  }\n\n  /**\n   * @param {string|symbol} event\n   * @param {(...args: any[]) => void} listener\n   * @returns {this}\n   */\n  removeListener (event, listener) {\n    return this.off(event, listener)\n  }\n\n  /**\n   * @param {Buffer|null} chunk\n   * @returns {boolean}\n   */\n  push (chunk) {\n    if (chunk) {\n      this[kBytesRead] += chunk.length\n      if (this[kConsume]) {\n        consumePush(this[kConsume], chunk)\n        return this[kReading] ? super.push(chunk) : true\n      }\n    }\n\n    return super.push(chunk)\n  }\n\n  /**\n   * Consumes and returns the body as a string.\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-text\n   * @returns {Promise<string>}\n   */\n  text () {\n    return consume(this, 'text')\n  }\n\n  /**\n   * Consumes and returns the body as a JavaScript Object.\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-json\n   * @returns {Promise<unknown>}\n   */\n  json () {\n    return consume(this, 'json')\n  }\n\n  /**\n   * Consumes and returns the body as a Blob\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-blob\n   * @returns {Promise<Blob>}\n   */\n  blob () {\n    return consume(this, 'blob')\n  }\n\n  /**\n   * Consumes and returns the body as an Uint8Array.\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-bytes\n   * @returns {Promise<Uint8Array>}\n   */\n  bytes () {\n    return consume(this, 'bytes')\n  }\n\n  /**\n   * Consumes and returns the body as an ArrayBuffer.\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-arraybuffer\n   * @returns {Promise<ArrayBuffer>}\n   */\n  arrayBuffer () {\n    return consume(this, 'arrayBuffer')\n  }\n\n  /**\n   * Not implemented\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-formdata\n   * @throws {NotSupportedError}\n   */\n  async formData () {\n    // TODO: Implement.\n    throw new NotSupportedError()\n  }\n\n  /**\n   * Returns true if the body is not null and the body has been consumed.\n   * Otherwise, returns false.\n   *\n   * @see https://fetch.spec.whatwg.org/#dom-body-bodyused\n   * @readonly\n   * @returns {boolean}\n   */\n  get bodyUsed () {\n    return util.isDisturbed(this)\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#dom-body-body\n   * @readonly\n   * @returns {ReadableStream}\n   */\n  get body () {\n    if (!this[kBody]) {\n      this[kBody] = ReadableStreamFrom(this)\n      if (this[kConsume]) {\n        // TODO: Is this the best way to force a lock?\n        this[kBody].getReader() // Ensure stream is locked.\n        assert(this[kBody].locked)\n      }\n    }\n    return this[kBody]\n  }\n\n  /**\n   * Dumps the response body by reading `limit` number of bytes.\n   * @param {object} opts\n   * @param {number} [opts.limit = 131072] Number of bytes to read.\n   * @param {AbortSignal} [opts.signal] An AbortSignal to cancel the dump.\n   * @returns {Promise<null>}\n   */\n  dump (opts) {\n    const signal = opts?.signal\n\n    if (signal != null && (typeof signal !== 'object' || !('aborted' in signal))) {\n      return Promise.reject(new InvalidArgumentError('signal must be an AbortSignal'))\n    }\n\n    const limit = opts?.limit && Number.isFinite(opts.limit)\n      ? opts.limit\n      : 128 * 1024\n\n    if (signal?.aborted) {\n      return Promise.reject(signal.reason ?? new AbortError())\n    }\n\n    if (this._readableState.closeEmitted) {\n      return Promise.resolve(null)\n    }\n\n    return new Promise((resolve, reject) => {\n      if (\n        (this[kContentLength] && (this[kContentLength] > limit)) ||\n        this[kBytesRead] > limit\n      ) {\n        this.destroy(new AbortError())\n      }\n\n      if (signal) {\n        const onAbort = () => {\n          this.destroy(signal.reason ?? new AbortError())\n        }\n        signal.addEventListener('abort', onAbort)\n        this\n          .on('close', function () {\n            signal.removeEventListener('abort', onAbort)\n            if (signal.aborted) {\n              reject(signal.reason ?? new AbortError())\n            } else {\n              resolve(null)\n            }\n          })\n      } else {\n        this.on('close', resolve)\n      }\n\n      this\n        .on('error', noop)\n        .on('data', () => {\n          if (this[kBytesRead] > limit) {\n            this.destroy()\n          }\n        })\n        .resume()\n    })\n  }\n\n  /**\n   * @param {BufferEncoding} encoding\n   * @returns {this}\n   */\n  setEncoding (encoding) {\n    if (Buffer.isEncoding(encoding)) {\n      this._readableState.encoding = encoding\n    }\n    return this\n  }\n}\n\n/**\n * @see https://streams.spec.whatwg.org/#readablestream-locked\n * @param {BodyReadable} bodyReadable\n * @returns {boolean}\n */\nfunction isLocked (bodyReadable) {\n  // Consume is an implicit lock.\n  return bodyReadable[kBody]?.locked === true || bodyReadable[kConsume] !== null\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#body-unusable\n * @param {BodyReadable} bodyReadable\n * @returns {boolean}\n */\nfunction isUnusable (bodyReadable) {\n  return util.isDisturbed(bodyReadable) || isLocked(bodyReadable)\n}\n\n/**\n * @typedef {'text' | 'json' | 'blob' | 'bytes' | 'arrayBuffer'} ConsumeType\n */\n\n/**\n * @template {ConsumeType} T\n * @typedef {T extends 'text' ? string :\n *           T extends 'json' ? unknown :\n *           T extends 'blob' ? Blob :\n *           T extends 'arrayBuffer' ? ArrayBuffer :\n *           T extends 'bytes' ? Uint8Array :\n *           never\n * } ConsumeReturnType\n */\n/**\n * @typedef {object} Consume\n * @property {ConsumeType} type\n * @property {BodyReadable} stream\n * @property {((value?: any) => void)} resolve\n * @property {((err: Error) => void)} reject\n * @property {number} length\n * @property {Buffer[]} body\n */\n\n/**\n * @template {ConsumeType} T\n * @param {BodyReadable} stream\n * @param {T} type\n * @returns {Promise<ConsumeReturnType<T>>}\n */\nfunction consume (stream, type) {\n  assert(!stream[kConsume])\n\n  return new Promise((resolve, reject) => {\n    if (isUnusable(stream)) {\n      const rState = stream._readableState\n      if (rState.destroyed && rState.closeEmitted === false) {\n        stream\n          .on('error', reject)\n          .on('close', () => {\n            reject(new TypeError('unusable'))\n          })\n      } else {\n        reject(rState.errored ?? new TypeError('unusable'))\n      }\n    } else {\n      queueMicrotask(() => {\n        stream[kConsume] = {\n          type,\n          stream,\n          resolve,\n          reject,\n          length: 0,\n          body: []\n        }\n\n        stream\n          .on('error', function (err) {\n            consumeFinish(this[kConsume], err)\n          })\n          .on('close', function () {\n            if (this[kConsume].body !== null) {\n              consumeFinish(this[kConsume], new RequestAbortedError())\n            }\n          })\n\n        consumeStart(stream[kConsume])\n      })\n    }\n  })\n}\n\n/**\n * @param {Consume} consume\n * @returns {void}\n */\nfunction consumeStart (consume) {\n  if (consume.body === null) {\n    return\n  }\n\n  const { _readableState: state } = consume.stream\n\n  if (state.bufferIndex) {\n    const start = state.bufferIndex\n    const end = state.buffer.length\n    for (let n = start; n < end; n++) {\n      consumePush(consume, state.buffer[n])\n    }\n  } else {\n    for (const chunk of state.buffer) {\n      consumePush(consume, chunk)\n    }\n  }\n\n  if (state.endEmitted) {\n    consumeEnd(this[kConsume], this._readableState.encoding)\n  } else {\n    consume.stream.on('end', function () {\n      consumeEnd(this[kConsume], this._readableState.encoding)\n    })\n  }\n\n  consume.stream.resume()\n\n  while (consume.stream.read() != null) {\n    // Loop\n  }\n}\n\n/**\n * @param {Buffer[]} chunks\n * @param {number} length\n * @param {BufferEncoding} [encoding='utf8']\n * @returns {string}\n */\nfunction chunksDecode (chunks, length, encoding) {\n  if (chunks.length === 0 || length === 0) {\n    return ''\n  }\n  const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, length)\n  const bufferLength = buffer.length\n\n  // Skip BOM.\n  const start =\n    bufferLength > 2 &&\n    buffer[0] === 0xef &&\n    buffer[1] === 0xbb &&\n    buffer[2] === 0xbf\n      ? 3\n      : 0\n  if (!encoding || encoding === 'utf8' || encoding === 'utf-8') {\n    return buffer.utf8Slice(start, bufferLength)\n  } else {\n    return buffer.subarray(start, bufferLength).toString(encoding)\n  }\n}\n\n/**\n * @param {Buffer[]} chunks\n * @param {number} length\n * @returns {Uint8Array}\n */\nfunction chunksConcat (chunks, length) {\n  if (chunks.length === 0 || length === 0) {\n    return new Uint8Array(0)\n  }\n  if (chunks.length === 1) {\n    // fast-path\n    return new Uint8Array(chunks[0])\n  }\n  const buffer = new Uint8Array(Buffer.allocUnsafeSlow(length).buffer)\n\n  let offset = 0\n  for (let i = 0; i < chunks.length; ++i) {\n    const chunk = chunks[i]\n    buffer.set(chunk, offset)\n    offset += chunk.length\n  }\n\n  return buffer\n}\n\n/**\n * @param {Consume} consume\n * @param {BufferEncoding} encoding\n * @returns {void}\n */\nfunction consumeEnd (consume, encoding) {\n  const { type, body, resolve, stream, length } = consume\n\n  try {\n    if (type === 'text') {\n      resolve(chunksDecode(body, length, encoding))\n    } else if (type === 'json') {\n      resolve(JSON.parse(chunksDecode(body, length, encoding)))\n    } else if (type === 'arrayBuffer') {\n      resolve(chunksConcat(body, length).buffer)\n    } else if (type === 'blob') {\n      resolve(new Blob(body, { type: stream[kContentType] }))\n    } else if (type === 'bytes') {\n      resolve(chunksConcat(body, length))\n    }\n\n    consumeFinish(consume)\n  } catch (err) {\n    stream.destroy(err)\n  }\n}\n\n/**\n * @param {Consume} consume\n * @param {Buffer} chunk\n * @returns {void}\n */\nfunction consumePush (consume, chunk) {\n  consume.length += chunk.length\n  consume.body.push(chunk)\n}\n\n/**\n * @param {Consume} consume\n * @param {Error} [err]\n * @returns {void}\n */\nfunction consumeFinish (consume, err) {\n  if (consume.body === null) {\n    return\n  }\n\n  if (err) {\n    consume.reject(err)\n  } else {\n    consume.resolve()\n  }\n\n  // Reset the consume object to allow for garbage collection.\n  consume.type = null\n  consume.stream = null\n  consume.resolve = null\n  consume.reject = null\n  consume.length = 0\n  consume.body = null\n}\n\nmodule.exports = {\n  Readable: BodyReadable,\n  chunksDecode\n}\n"
  },
  {
    "path": "lib/cache/memory-cache-store.js",
    "content": "'use strict'\n\nconst { Writable } = require('node:stream')\nconst { EventEmitter } = require('node:events')\nconst { assertCacheKey, assertCacheValue } = require('../util/cache.js')\n\n/**\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheKey} CacheKey\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheValue} CacheValue\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore\n * @typedef {import('../../types/cache-interceptor.d.ts').default.GetResult} GetResult\n */\n\n/**\n * @implements {CacheStore}\n * @extends {EventEmitter}\n */\nclass MemoryCacheStore extends EventEmitter {\n  #maxCount = 1024\n  #maxSize = 104857600 // 100MB\n  #maxEntrySize = 5242880 // 5MB\n\n  #size = 0\n  #count = 0\n  #entries = new Map()\n  #hasEmittedMaxSizeEvent = false\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.MemoryCacheStoreOpts | undefined} [opts]\n   */\n  constructor (opts) {\n    super()\n    if (opts) {\n      if (typeof opts !== 'object') {\n        throw new TypeError('MemoryCacheStore options must be an object')\n      }\n\n      if (opts.maxCount !== undefined) {\n        if (\n          typeof opts.maxCount !== 'number' ||\n          !Number.isInteger(opts.maxCount) ||\n          opts.maxCount < 0\n        ) {\n          throw new TypeError('MemoryCacheStore options.maxCount must be a non-negative integer')\n        }\n        this.#maxCount = opts.maxCount\n      }\n\n      if (opts.maxSize !== undefined) {\n        if (\n          typeof opts.maxSize !== 'number' ||\n          !Number.isInteger(opts.maxSize) ||\n          opts.maxSize < 0\n        ) {\n          throw new TypeError('MemoryCacheStore options.maxSize must be a non-negative integer')\n        }\n        this.#maxSize = opts.maxSize\n      }\n\n      if (opts.maxEntrySize !== undefined) {\n        if (\n          typeof opts.maxEntrySize !== 'number' ||\n          !Number.isInteger(opts.maxEntrySize) ||\n          opts.maxEntrySize < 0\n        ) {\n          throw new TypeError('MemoryCacheStore options.maxEntrySize must be a non-negative integer')\n        }\n        this.#maxEntrySize = opts.maxEntrySize\n      }\n    }\n  }\n\n  /**\n   * Get the current size of the cache in bytes\n   * @returns {number} The current size of the cache in bytes\n   */\n  get size () {\n    return this.#size\n  }\n\n  /**\n   * Check if the cache is full (either max size or max count reached)\n   * @returns {boolean} True if the cache is full, false otherwise\n   */\n  isFull () {\n    return this.#size >= this.#maxSize || this.#count >= this.#maxCount\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req\n   * @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}\n   */\n  get (key) {\n    assertCacheKey(key)\n\n    const topLevelKey = `${key.origin}:${key.path}`\n\n    const now = Date.now()\n    const entries = this.#entries.get(topLevelKey)\n\n    const entry = entries ? findEntry(key, entries, now) : null\n\n    return entry == null\n      ? undefined\n      : {\n          statusMessage: entry.statusMessage,\n          statusCode: entry.statusCode,\n          headers: entry.headers,\n          body: entry.body,\n          vary: entry.vary ? entry.vary : undefined,\n          etag: entry.etag,\n          cacheControlDirectives: entry.cacheControlDirectives,\n          cachedAt: entry.cachedAt,\n          staleAt: entry.staleAt,\n          deleteAt: entry.deleteAt\n        }\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} val\n   * @returns {Writable | undefined}\n   */\n  createWriteStream (key, val) {\n    assertCacheKey(key)\n    assertCacheValue(val)\n\n    const topLevelKey = `${key.origin}:${key.path}`\n\n    const store = this\n    const entry = { ...key, ...val, body: [], size: 0 }\n\n    return new Writable({\n      write (chunk, encoding, callback) {\n        if (typeof chunk === 'string') {\n          chunk = Buffer.from(chunk, encoding)\n        }\n\n        entry.size += chunk.byteLength\n\n        if (entry.size >= store.#maxEntrySize) {\n          this.destroy()\n        } else {\n          entry.body.push(chunk)\n        }\n\n        callback(null)\n      },\n      final (callback) {\n        let entries = store.#entries.get(topLevelKey)\n        if (!entries) {\n          entries = []\n          store.#entries.set(topLevelKey, entries)\n        }\n        const previousEntry = findEntry(key, entries, Date.now())\n        if (previousEntry) {\n          const index = entries.indexOf(previousEntry)\n          entries.splice(index, 1, entry)\n          store.#size -= previousEntry.size\n        } else {\n          entries.push(entry)\n          store.#count += 1\n        }\n\n        store.#size += entry.size\n\n        // Check if cache is full and emit event if needed\n        if (store.#size > store.#maxSize || store.#count > store.#maxCount) {\n          // Emit maxSizeExceeded event if we haven't already\n          if (!store.#hasEmittedMaxSizeEvent) {\n            store.emit('maxSizeExceeded', {\n              size: store.#size,\n              maxSize: store.#maxSize,\n              count: store.#count,\n              maxCount: store.#maxCount\n            })\n            store.#hasEmittedMaxSizeEvent = true\n          }\n\n          // Perform eviction\n          for (const [key, entries] of store.#entries) {\n            for (const entry of entries.splice(0, entries.length / 2)) {\n              store.#size -= entry.size\n              store.#count -= 1\n            }\n            if (entries.length === 0) {\n              store.#entries.delete(key)\n            }\n          }\n\n          // Reset the event flag after eviction\n          if (store.#size < store.#maxSize && store.#count < store.#maxCount) {\n            store.#hasEmittedMaxSizeEvent = false\n          }\n        }\n\n        callback(null)\n      }\n    })\n  }\n\n  /**\n   * @param {CacheKey} key\n   */\n  delete (key) {\n    if (typeof key !== 'object') {\n      throw new TypeError(`expected key to be object, got ${typeof key}`)\n    }\n\n    const topLevelKey = `${key.origin}:${key.path}`\n\n    for (const entry of this.#entries.get(topLevelKey) ?? []) {\n      this.#size -= entry.size\n      this.#count -= 1\n    }\n    this.#entries.delete(topLevelKey)\n  }\n}\n\nfunction findEntry (key, entries, now) {\n  return entries.find((entry) => (\n    entry.deleteAt > now &&\n    entry.method === key.method &&\n    (entry.vary == null || Object.keys(entry.vary).every(headerName => {\n      if (entry.vary[headerName] === null) {\n        return key.headers[headerName] === undefined\n      }\n\n      return entry.vary[headerName] === key.headers[headerName]\n    }))\n  ))\n}\n\nmodule.exports = MemoryCacheStore\n"
  },
  {
    "path": "lib/cache/sqlite-cache-store.js",
    "content": "'use strict'\n\nconst { Writable } = require('node:stream')\nconst { assertCacheKey, assertCacheValue } = require('../util/cache.js')\n\nlet DatabaseSync\n\nconst VERSION = 3\n\n// 2gb\nconst MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000\n\n/**\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore\n * @implements {CacheStore}\n *\n * @typedef {{\n *  id: Readonly<number>,\n *  body?: Uint8Array\n *  statusCode: number\n *  statusMessage: string\n *  headers?: string\n *  vary?: string\n *  etag?: string\n *  cacheControlDirectives?: string\n *  cachedAt: number\n *  staleAt: number\n *  deleteAt: number\n * }} SqliteStoreValue\n */\nmodule.exports = class SqliteCacheStore {\n  #maxEntrySize = MAX_ENTRY_SIZE\n  #maxCount = Infinity\n\n  /**\n   * @type {import('node:sqlite').DatabaseSync}\n   */\n  #db\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #getValuesQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #updateValueQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #insertValueQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #deleteExpiredValuesQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #deleteByUrlQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync}\n   */\n  #countEntriesQuery\n\n  /**\n   * @type {import('node:sqlite').StatementSync | null}\n   */\n  #deleteOldValuesQuery\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts | undefined} opts\n   */\n  constructor (opts) {\n    if (opts) {\n      if (typeof opts !== 'object') {\n        throw new TypeError('SqliteCacheStore options must be an object')\n      }\n\n      if (opts.maxEntrySize !== undefined) {\n        if (\n          typeof opts.maxEntrySize !== 'number' ||\n          !Number.isInteger(opts.maxEntrySize) ||\n          opts.maxEntrySize < 0\n        ) {\n          throw new TypeError('SqliteCacheStore options.maxEntrySize must be a non-negative integer')\n        }\n\n        if (opts.maxEntrySize > MAX_ENTRY_SIZE) {\n          throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')\n        }\n\n        this.#maxEntrySize = opts.maxEntrySize\n      }\n\n      if (opts.maxCount !== undefined) {\n        if (\n          typeof opts.maxCount !== 'number' ||\n          !Number.isInteger(opts.maxCount) ||\n          opts.maxCount < 0\n        ) {\n          throw new TypeError('SqliteCacheStore options.maxCount must be a non-negative integer')\n        }\n        this.#maxCount = opts.maxCount\n      }\n    }\n\n    if (!DatabaseSync) {\n      DatabaseSync = require('node:sqlite').DatabaseSync\n    }\n    this.#db = new DatabaseSync(opts?.location ?? ':memory:')\n\n    this.#db.exec(`\n      PRAGMA journal_mode = WAL;\n      PRAGMA synchronous = NORMAL;\n      PRAGMA temp_store = memory;\n      PRAGMA optimize;\n\n      CREATE TABLE IF NOT EXISTS cacheInterceptorV${VERSION} (\n        -- Data specific to us\n        id INTEGER PRIMARY KEY AUTOINCREMENT,\n        url TEXT NOT NULL,\n        method TEXT NOT NULL,\n\n        -- Data returned to the interceptor\n        body BUF NULL,\n        deleteAt INTEGER NOT NULL,\n        statusCode INTEGER NOT NULL,\n        statusMessage TEXT NOT NULL,\n        headers TEXT NULL,\n        cacheControlDirectives TEXT NULL,\n        etag TEXT NULL,\n        vary TEXT NULL,\n        cachedAt INTEGER NOT NULL,\n        staleAt INTEGER NOT NULL\n      );\n\n      CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_getValuesQuery ON cacheInterceptorV${VERSION}(url, method, deleteAt);\n      CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteByUrlQuery ON cacheInterceptorV${VERSION}(deleteAt);\n    `)\n\n    this.#getValuesQuery = this.#db.prepare(`\n      SELECT\n        id,\n        body,\n        deleteAt,\n        statusCode,\n        statusMessage,\n        headers,\n        etag,\n        cacheControlDirectives,\n        vary,\n        cachedAt,\n        staleAt\n      FROM cacheInterceptorV${VERSION}\n      WHERE\n        url = ?\n        AND method = ?\n      ORDER BY\n        deleteAt ASC\n    `)\n\n    this.#updateValueQuery = this.#db.prepare(`\n      UPDATE cacheInterceptorV${VERSION} SET\n        body = ?,\n        deleteAt = ?,\n        statusCode = ?,\n        statusMessage = ?,\n        headers = ?,\n        etag = ?,\n        cacheControlDirectives = ?,\n        cachedAt = ?,\n        staleAt = ?\n      WHERE\n        id = ?\n    `)\n\n    this.#insertValueQuery = this.#db.prepare(`\n      INSERT INTO cacheInterceptorV${VERSION} (\n        url,\n        method,\n        body,\n        deleteAt,\n        statusCode,\n        statusMessage,\n        headers,\n        etag,\n        cacheControlDirectives,\n        vary,\n        cachedAt,\n        staleAt\n      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `)\n\n    this.#deleteByUrlQuery = this.#db.prepare(\n      `DELETE FROM cacheInterceptorV${VERSION} WHERE url = ?`\n    )\n\n    this.#countEntriesQuery = this.#db.prepare(\n      `SELECT COUNT(*) AS total FROM cacheInterceptorV${VERSION}`\n    )\n\n    this.#deleteExpiredValuesQuery = this.#db.prepare(\n      `DELETE FROM cacheInterceptorV${VERSION} WHERE deleteAt <= ?`\n    )\n\n    this.#deleteOldValuesQuery = this.#maxCount === Infinity\n      ? null\n      : this.#db.prepare(`\n        DELETE FROM cacheInterceptorV${VERSION}\n        WHERE id IN (\n          SELECT\n            id\n          FROM cacheInterceptorV${VERSION}\n          ORDER BY cachedAt DESC\n          LIMIT ?\n        )\n      `)\n  }\n\n  close () {\n    this.#db.close()\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @returns {(import('../../types/cache-interceptor.d.ts').default.GetResult & { body?: Buffer }) | undefined}\n   */\n  get (key) {\n    assertCacheKey(key)\n\n    const value = this.#findValue(key)\n    return value\n      ? {\n          body: value.body ? Buffer.from(value.body.buffer, value.body.byteOffset, value.body.byteLength) : undefined,\n          statusCode: value.statusCode,\n          statusMessage: value.statusMessage,\n          headers: value.headers ? JSON.parse(value.headers) : undefined,\n          etag: value.etag ? value.etag : undefined,\n          vary: value.vary ? JSON.parse(value.vary) : undefined,\n          cacheControlDirectives: value.cacheControlDirectives\n            ? JSON.parse(value.cacheControlDirectives)\n            : undefined,\n          cachedAt: value.cachedAt,\n          staleAt: value.staleAt,\n          deleteAt: value.deleteAt\n        }\n      : undefined\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: null | Buffer | Array<Buffer>}} value\n   */\n  set (key, value) {\n    assertCacheKey(key)\n\n    const url = this.#makeValueUrl(key)\n    const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body\n    const size = body?.byteLength\n\n    if (size && size > this.#maxEntrySize) {\n      return\n    }\n\n    const existingValue = this.#findValue(key, true)\n    if (existingValue) {\n      // Updating an existing response, let's overwrite it\n      this.#updateValueQuery.run(\n        body,\n        value.deleteAt,\n        value.statusCode,\n        value.statusMessage,\n        value.headers ? JSON.stringify(value.headers) : null,\n        value.etag ? value.etag : null,\n        value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,\n        value.cachedAt,\n        value.staleAt,\n        existingValue.id\n      )\n    } else {\n      this.#prune()\n      // New response, let's insert it\n      this.#insertValueQuery.run(\n        url,\n        key.method,\n        body,\n        value.deleteAt,\n        value.statusCode,\n        value.statusMessage,\n        value.headers ? JSON.stringify(value.headers) : null,\n        value.etag ? value.etag : null,\n        value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,\n        value.vary ? JSON.stringify(value.vary) : null,\n        value.cachedAt,\n        value.staleAt\n      )\n    }\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} value\n   * @returns {Writable | undefined}\n   */\n  createWriteStream (key, value) {\n    assertCacheKey(key)\n    assertCacheValue(value)\n\n    let size = 0\n    /**\n     * @type {Buffer[] | null}\n     */\n    const body = []\n    const store = this\n\n    return new Writable({\n      decodeStrings: true,\n      write (chunk, encoding, callback) {\n        size += chunk.byteLength\n\n        if (size < store.#maxEntrySize) {\n          body.push(chunk)\n        } else {\n          this.destroy()\n        }\n\n        callback()\n      },\n      final (callback) {\n        store.set(key, { ...value, body })\n        callback()\n      }\n    })\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   */\n  delete (key) {\n    if (typeof key !== 'object') {\n      throw new TypeError(`expected key to be object, got ${typeof key}`)\n    }\n\n    this.#deleteByUrlQuery.run(this.#makeValueUrl(key))\n  }\n\n  #prune () {\n    if (Number.isFinite(this.#maxCount) && this.size <= this.#maxCount) {\n      return 0\n    }\n\n    {\n      const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes\n      if (removed) {\n        return removed\n      }\n    }\n\n    {\n      const removed = this.#deleteOldValuesQuery?.run(Math.max(Math.floor(this.#maxCount * 0.1), 1)).changes\n      if (removed) {\n        return removed\n      }\n    }\n\n    return 0\n  }\n\n  /**\n   * Counts the number of rows in the cache\n   * @returns {Number}\n   */\n  get size () {\n    const { total } = this.#countEntriesQuery.get()\n    return total\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @returns {string}\n   */\n  #makeValueUrl (key) {\n    return `${key.origin}/${key.path}`\n  }\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key\n   * @param {boolean} [canBeExpired=false]\n   * @returns {SqliteStoreValue | undefined}\n   */\n  #findValue (key, canBeExpired = false) {\n    const url = this.#makeValueUrl(key)\n    const { headers, method } = key\n\n    /**\n     * @type {SqliteStoreValue[]}\n     */\n    const values = this.#getValuesQuery.all(url, method)\n\n    if (values.length === 0) {\n      return undefined\n    }\n\n    const now = Date.now()\n    for (const value of values) {\n      if (now >= value.deleteAt && !canBeExpired) {\n        return undefined\n      }\n\n      let matches = true\n\n      if (value.vary) {\n        const vary = JSON.parse(value.vary)\n\n        for (const header in vary) {\n          if (!headerValueEquals(headers[header], vary[header])) {\n            matches = false\n            break\n          }\n        }\n      }\n\n      if (matches) {\n        return value\n      }\n    }\n\n    return undefined\n  }\n}\n\n/**\n * @param {string|string[]|null|undefined} lhs\n * @param {string|string[]|null|undefined} rhs\n * @returns {boolean}\n */\nfunction headerValueEquals (lhs, rhs) {\n  if (lhs == null && rhs == null) {\n    return true\n  }\n\n  if ((lhs == null && rhs != null) ||\n      (lhs != null && rhs == null)) {\n    return false\n  }\n\n  if (Array.isArray(lhs) && Array.isArray(rhs)) {\n    if (lhs.length !== rhs.length) {\n      return false\n    }\n\n    return lhs.every((x, i) => x === rhs[i])\n  }\n\n  return lhs === rhs\n}\n"
  },
  {
    "path": "lib/core/connect.js",
    "content": "'use strict'\n\nconst net = require('node:net')\nconst assert = require('node:assert')\nconst util = require('./util')\nconst { InvalidArgumentError } = require('./errors')\n\nlet tls // include tls conditionally since it is not always available\n\n// TODO: session re-use does not wait for the first\n// connection to resolve the session and might therefore\n// resolve the same servername multiple times even when\n// re-use is enabled.\n\nconst SessionCache = class WeakSessionCache {\n  constructor (maxCachedSessions) {\n    this._maxCachedSessions = maxCachedSessions\n    this._sessionCache = new Map()\n    this._sessionRegistry = new FinalizationRegistry((key) => {\n      if (this._sessionCache.size < this._maxCachedSessions) {\n        return\n      }\n\n      const ref = this._sessionCache.get(key)\n      if (ref !== undefined && ref.deref() === undefined) {\n        this._sessionCache.delete(key)\n      }\n    })\n  }\n\n  get (sessionKey) {\n    const ref = this._sessionCache.get(sessionKey)\n    return ref ? ref.deref() : null\n  }\n\n  set (sessionKey, session) {\n    if (this._maxCachedSessions === 0) {\n      return\n    }\n\n    this._sessionCache.set(sessionKey, new WeakRef(session))\n    this._sessionRegistry.register(session, sessionKey)\n  }\n}\n\nfunction buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {\n  if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {\n    throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')\n  }\n\n  const options = { path: socketPath, ...opts }\n  const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)\n  timeout = timeout == null ? 10e3 : timeout\n  allowH2 = allowH2 != null ? allowH2 : false\n  return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {\n    let socket\n    if (protocol === 'https:') {\n      if (!tls) {\n        tls = require('node:tls')\n      }\n      servername = servername || options.servername || util.getServerName(host) || null\n\n      const sessionKey = servername || hostname\n      assert(sessionKey)\n\n      const session = customSession || sessionCache.get(sessionKey) || null\n\n      port = port || 443\n\n      socket = tls.connect({\n        highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...\n        ...options,\n        servername,\n        session,\n        localAddress,\n        ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],\n        socket: httpSocket, // upgrade socket connection\n        port,\n        host: hostname\n      })\n\n      socket\n        .on('session', function (session) {\n          // TODO (fix): Can a session become invalid once established? Don't think so?\n          sessionCache.set(sessionKey, session)\n        })\n    } else {\n      assert(!httpSocket, 'httpSocket can only be sent on TLS update')\n\n      port = port || 80\n\n      socket = net.connect({\n        highWaterMark: 64 * 1024, // Same as nodejs fs streams.\n        ...options,\n        localAddress,\n        port,\n        host: hostname\n      })\n      if (useH2c === true) {\n        socket.alpnProtocol = 'h2'\n      }\n    }\n\n    // Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket\n    if (options.keepAlive == null || options.keepAlive) {\n      const keepAliveInitialDelay = options.keepAliveInitialDelay === undefined ? 60e3 : options.keepAliveInitialDelay\n      socket.setKeepAlive(true, keepAliveInitialDelay)\n    }\n\n    const clearConnectTimeout = util.setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })\n\n    socket\n      .setNoDelay(true)\n      .once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {\n        queueMicrotask(clearConnectTimeout)\n\n        if (callback) {\n          const cb = callback\n          callback = null\n          cb(null, this)\n        }\n      })\n      .on('error', function (err) {\n        queueMicrotask(clearConnectTimeout)\n\n        if (callback) {\n          const cb = callback\n          callback = null\n          cb(err)\n        }\n      })\n\n    return socket\n  }\n}\n\nmodule.exports = buildConnector\n"
  },
  {
    "path": "lib/core/constants.js",
    "content": "'use strict'\n\n/**\n * @see https://developer.mozilla.org/docs/Web/HTTP/Headers\n */\nconst wellknownHeaderNames = /** @type {const} */ ([\n  'Accept',\n  'Accept-Encoding',\n  'Accept-Language',\n  'Accept-Ranges',\n  'Access-Control-Allow-Credentials',\n  'Access-Control-Allow-Headers',\n  'Access-Control-Allow-Methods',\n  'Access-Control-Allow-Origin',\n  'Access-Control-Expose-Headers',\n  'Access-Control-Max-Age',\n  'Access-Control-Request-Headers',\n  'Access-Control-Request-Method',\n  'Age',\n  'Allow',\n  'Alt-Svc',\n  'Alt-Used',\n  'Authorization',\n  'Cache-Control',\n  'Clear-Site-Data',\n  'Connection',\n  'Content-Disposition',\n  'Content-Encoding',\n  'Content-Language',\n  'Content-Length',\n  'Content-Location',\n  'Content-Range',\n  'Content-Security-Policy',\n  'Content-Security-Policy-Report-Only',\n  'Content-Type',\n  'Cookie',\n  'Cross-Origin-Embedder-Policy',\n  'Cross-Origin-Opener-Policy',\n  'Cross-Origin-Resource-Policy',\n  'Date',\n  'Device-Memory',\n  'Downlink',\n  'ECT',\n  'ETag',\n  'Expect',\n  'Expect-CT',\n  'Expires',\n  'Forwarded',\n  'From',\n  'Host',\n  'If-Match',\n  'If-Modified-Since',\n  'If-None-Match',\n  'If-Range',\n  'If-Unmodified-Since',\n  'Keep-Alive',\n  'Last-Modified',\n  'Link',\n  'Location',\n  'Max-Forwards',\n  'Origin',\n  'Permissions-Policy',\n  'Pragma',\n  'Proxy-Authenticate',\n  'Proxy-Authorization',\n  'RTT',\n  'Range',\n  'Referer',\n  'Referrer-Policy',\n  'Refresh',\n  'Retry-After',\n  'Sec-WebSocket-Accept',\n  'Sec-WebSocket-Extensions',\n  'Sec-WebSocket-Key',\n  'Sec-WebSocket-Protocol',\n  'Sec-WebSocket-Version',\n  'Server',\n  'Server-Timing',\n  'Service-Worker-Allowed',\n  'Service-Worker-Navigation-Preload',\n  'Set-Cookie',\n  'SourceMap',\n  'Strict-Transport-Security',\n  'Supports-Loading-Mode',\n  'TE',\n  'Timing-Allow-Origin',\n  'Trailer',\n  'Transfer-Encoding',\n  'Upgrade',\n  'Upgrade-Insecure-Requests',\n  'User-Agent',\n  'Vary',\n  'Via',\n  'WWW-Authenticate',\n  'X-Content-Type-Options',\n  'X-DNS-Prefetch-Control',\n  'X-Frame-Options',\n  'X-Permitted-Cross-Domain-Policies',\n  'X-Powered-By',\n  'X-Requested-With',\n  'X-XSS-Protection'\n])\n\n/** @type {Record<typeof wellknownHeaderNames[number]|Lowercase<typeof wellknownHeaderNames[number]>, string>} */\nconst headerNameLowerCasedRecord = {}\n\n// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.\nObject.setPrototypeOf(headerNameLowerCasedRecord, null)\n\n/**\n * @type {Record<Lowercase<typeof wellknownHeaderNames[number]>, Buffer>}\n */\nconst wellknownHeaderNameBuffers = {}\n\n// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.\nObject.setPrototypeOf(wellknownHeaderNameBuffers, null)\n\n/**\n * @param {string} header Lowercased header\n * @returns {Buffer}\n */\nfunction getHeaderNameAsBuffer (header) {\n  let buffer = wellknownHeaderNameBuffers[header]\n\n  if (buffer === undefined) {\n    buffer = Buffer.from(header)\n  }\n\n  return buffer\n}\n\nfor (let i = 0; i < wellknownHeaderNames.length; ++i) {\n  const key = wellknownHeaderNames[i]\n  const lowerCasedKey = key.toLowerCase()\n  headerNameLowerCasedRecord[key] = headerNameLowerCasedRecord[lowerCasedKey] =\n    lowerCasedKey\n}\n\nmodule.exports = {\n  wellknownHeaderNames,\n  headerNameLowerCasedRecord,\n  getHeaderNameAsBuffer\n}\n"
  },
  {
    "path": "lib/core/diagnostics.js",
    "content": "'use strict'\n\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst util = require('node:util')\n\nconst undiciDebugLog = util.debuglog('undici')\nconst fetchDebuglog = util.debuglog('fetch')\nconst websocketDebuglog = util.debuglog('websocket')\n\nconst channels = {\n  // Client\n  beforeConnect: diagnosticsChannel.channel('undici:client:beforeConnect'),\n  connected: diagnosticsChannel.channel('undici:client:connected'),\n  connectError: diagnosticsChannel.channel('undici:client:connectError'),\n  sendHeaders: diagnosticsChannel.channel('undici:client:sendHeaders'),\n  // Request\n  create: diagnosticsChannel.channel('undici:request:create'),\n  bodySent: diagnosticsChannel.channel('undici:request:bodySent'),\n  bodyChunkSent: diagnosticsChannel.channel('undici:request:bodyChunkSent'),\n  bodyChunkReceived: diagnosticsChannel.channel('undici:request:bodyChunkReceived'),\n  headers: diagnosticsChannel.channel('undici:request:headers'),\n  trailers: diagnosticsChannel.channel('undici:request:trailers'),\n  error: diagnosticsChannel.channel('undici:request:error'),\n  // WebSocket\n  open: diagnosticsChannel.channel('undici:websocket:open'),\n  close: diagnosticsChannel.channel('undici:websocket:close'),\n  socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),\n  ping: diagnosticsChannel.channel('undici:websocket:ping'),\n  pong: diagnosticsChannel.channel('undici:websocket:pong'),\n  // ProxyAgent\n  proxyConnected: diagnosticsChannel.channel('undici:proxy:connected')\n}\n\nlet isTrackingClientEvents = false\n\nfunction trackClientEvents (debugLog = undiciDebugLog) {\n  if (isTrackingClientEvents) {\n    return\n  }\n\n  // Check if any of the channels already have subscribers to prevent duplicate subscriptions\n  // This can happen when both Node.js built-in undici and undici as a dependency are present\n  if (channels.beforeConnect.hasSubscribers || channels.connected.hasSubscribers ||\n      channels.connectError.hasSubscribers || channels.sendHeaders.hasSubscribers) {\n    isTrackingClientEvents = true\n    return\n  }\n\n  isTrackingClientEvents = true\n\n  diagnosticsChannel.subscribe('undici:client:beforeConnect',\n    evt => {\n      const {\n        connectParams: { version, protocol, port, host }\n      } = evt\n      debugLog(\n        'connecting to %s%s using %s%s',\n        host,\n        port ? `:${port}` : '',\n        protocol,\n        version\n      )\n    })\n\n  diagnosticsChannel.subscribe('undici:client:connected',\n    evt => {\n      const {\n        connectParams: { version, protocol, port, host }\n      } = evt\n      debugLog(\n        'connected to %s%s using %s%s',\n        host,\n        port ? `:${port}` : '',\n        protocol,\n        version\n      )\n    })\n\n  diagnosticsChannel.subscribe('undici:client:connectError',\n    evt => {\n      const {\n        connectParams: { version, protocol, port, host },\n        error\n      } = evt\n      debugLog(\n        'connection to %s%s using %s%s errored - %s',\n        host,\n        port ? `:${port}` : '',\n        protocol,\n        version,\n        error.message\n      )\n    })\n\n  diagnosticsChannel.subscribe('undici:client:sendHeaders',\n    evt => {\n      const {\n        request: { method, path, origin }\n      } = evt\n      debugLog('sending request to %s %s%s', method, origin, path)\n    })\n}\n\nlet isTrackingRequestEvents = false\n\nfunction trackRequestEvents (debugLog = undiciDebugLog) {\n  if (isTrackingRequestEvents) {\n    return\n  }\n\n  // Check if any of the channels already have subscribers to prevent duplicate subscriptions\n  // This can happen when both Node.js built-in undici and undici as a dependency are present\n  if (channels.headers.hasSubscribers || channels.trailers.hasSubscribers ||\n      channels.error.hasSubscribers) {\n    isTrackingRequestEvents = true\n    return\n  }\n\n  isTrackingRequestEvents = true\n\n  diagnosticsChannel.subscribe('undici:request:headers',\n    evt => {\n      const {\n        request: { method, path, origin },\n        response: { statusCode }\n      } = evt\n      debugLog(\n        'received response to %s %s%s - HTTP %d',\n        method,\n        origin,\n        path,\n        statusCode\n      )\n    })\n\n  diagnosticsChannel.subscribe('undici:request:trailers',\n    evt => {\n      const {\n        request: { method, path, origin }\n      } = evt\n      debugLog('trailers received from %s %s%s', method, origin, path)\n    })\n\n  diagnosticsChannel.subscribe('undici:request:error',\n    evt => {\n      const {\n        request: { method, path, origin },\n        error\n      } = evt\n      debugLog(\n        'request to %s %s%s errored - %s',\n        method,\n        origin,\n        path,\n        error.message\n      )\n    })\n}\n\nlet isTrackingWebSocketEvents = false\n\nfunction trackWebSocketEvents (debugLog = websocketDebuglog) {\n  if (isTrackingWebSocketEvents) {\n    return\n  }\n\n  // Check if any of the channels already have subscribers to prevent duplicate subscriptions\n  // This can happen when both Node.js built-in undici and undici as a dependency are present\n  if (channels.open.hasSubscribers || channels.close.hasSubscribers ||\n      channels.socketError.hasSubscribers || channels.ping.hasSubscribers ||\n      channels.pong.hasSubscribers) {\n    isTrackingWebSocketEvents = true\n    return\n  }\n\n  isTrackingWebSocketEvents = true\n\n  diagnosticsChannel.subscribe('undici:websocket:open',\n    evt => {\n      const {\n        address: { address, port }\n      } = evt\n      debugLog('connection opened %s%s', address, port ? `:${port}` : '')\n    })\n\n  diagnosticsChannel.subscribe('undici:websocket:close',\n    evt => {\n      const { websocket, code, reason } = evt\n      debugLog(\n        'closed connection to %s - %s %s',\n        websocket.url,\n        code,\n        reason\n      )\n    })\n\n  diagnosticsChannel.subscribe('undici:websocket:socket_error',\n    err => {\n      debugLog('connection errored - %s', err.message)\n    })\n\n  diagnosticsChannel.subscribe('undici:websocket:ping',\n    evt => {\n      debugLog('ping received')\n    })\n\n  diagnosticsChannel.subscribe('undici:websocket:pong',\n    evt => {\n      debugLog('pong received')\n    })\n}\n\nif (undiciDebugLog.enabled || fetchDebuglog.enabled) {\n  trackClientEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)\n  trackRequestEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)\n}\n\nif (websocketDebuglog.enabled) {\n  trackClientEvents(undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog)\n  trackWebSocketEvents(websocketDebuglog)\n}\n\nmodule.exports = {\n  channels\n}\n"
  },
  {
    "path": "lib/core/errors.js",
    "content": "'use strict'\n\nconst kUndiciError = Symbol.for('undici.error.UND_ERR')\nclass UndiciError extends Error {\n  constructor (message, options) {\n    super(message, options)\n    this.name = 'UndiciError'\n    this.code = 'UND_ERR'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kUndiciError] === true\n  }\n\n  get [kUndiciError] () {\n    return true\n  }\n}\n\nconst kConnectTimeoutError = Symbol.for('undici.error.UND_ERR_CONNECT_TIMEOUT')\nclass ConnectTimeoutError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'ConnectTimeoutError'\n    this.message = message || 'Connect Timeout Error'\n    this.code = 'UND_ERR_CONNECT_TIMEOUT'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kConnectTimeoutError] === true\n  }\n\n  get [kConnectTimeoutError] () {\n    return true\n  }\n}\n\nconst kHeadersTimeoutError = Symbol.for('undici.error.UND_ERR_HEADERS_TIMEOUT')\nclass HeadersTimeoutError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'HeadersTimeoutError'\n    this.message = message || 'Headers Timeout Error'\n    this.code = 'UND_ERR_HEADERS_TIMEOUT'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kHeadersTimeoutError] === true\n  }\n\n  get [kHeadersTimeoutError] () {\n    return true\n  }\n}\n\nconst kHeadersOverflowError = Symbol.for('undici.error.UND_ERR_HEADERS_OVERFLOW')\nclass HeadersOverflowError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'HeadersOverflowError'\n    this.message = message || 'Headers Overflow Error'\n    this.code = 'UND_ERR_HEADERS_OVERFLOW'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kHeadersOverflowError] === true\n  }\n\n  get [kHeadersOverflowError] () {\n    return true\n  }\n}\n\nconst kBodyTimeoutError = Symbol.for('undici.error.UND_ERR_BODY_TIMEOUT')\nclass BodyTimeoutError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'BodyTimeoutError'\n    this.message = message || 'Body Timeout Error'\n    this.code = 'UND_ERR_BODY_TIMEOUT'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kBodyTimeoutError] === true\n  }\n\n  get [kBodyTimeoutError] () {\n    return true\n  }\n}\n\nconst kInvalidArgumentError = Symbol.for('undici.error.UND_ERR_INVALID_ARG')\nclass InvalidArgumentError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'InvalidArgumentError'\n    this.message = message || 'Invalid Argument Error'\n    this.code = 'UND_ERR_INVALID_ARG'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kInvalidArgumentError] === true\n  }\n\n  get [kInvalidArgumentError] () {\n    return true\n  }\n}\n\nconst kInvalidReturnValueError = Symbol.for('undici.error.UND_ERR_INVALID_RETURN_VALUE')\nclass InvalidReturnValueError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'InvalidReturnValueError'\n    this.message = message || 'Invalid Return Value Error'\n    this.code = 'UND_ERR_INVALID_RETURN_VALUE'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kInvalidReturnValueError] === true\n  }\n\n  get [kInvalidReturnValueError] () {\n    return true\n  }\n}\n\nconst kAbortError = Symbol.for('undici.error.UND_ERR_ABORT')\nclass AbortError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'AbortError'\n    this.message = message || 'The operation was aborted'\n    this.code = 'UND_ERR_ABORT'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kAbortError] === true\n  }\n\n  get [kAbortError] () {\n    return true\n  }\n}\n\nconst kRequestAbortedError = Symbol.for('undici.error.UND_ERR_ABORTED')\nclass RequestAbortedError extends AbortError {\n  constructor (message) {\n    super(message)\n    this.name = 'AbortError'\n    this.message = message || 'Request aborted'\n    this.code = 'UND_ERR_ABORTED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kRequestAbortedError] === true\n  }\n\n  get [kRequestAbortedError] () {\n    return true\n  }\n}\n\nconst kInformationalError = Symbol.for('undici.error.UND_ERR_INFO')\nclass InformationalError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'InformationalError'\n    this.message = message || 'Request information'\n    this.code = 'UND_ERR_INFO'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kInformationalError] === true\n  }\n\n  get [kInformationalError] () {\n    return true\n  }\n}\n\nconst kRequestContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH')\nclass RequestContentLengthMismatchError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'RequestContentLengthMismatchError'\n    this.message = message || 'Request body length does not match content-length header'\n    this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kRequestContentLengthMismatchError] === true\n  }\n\n  get [kRequestContentLengthMismatchError] () {\n    return true\n  }\n}\n\nconst kResponseContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH')\nclass ResponseContentLengthMismatchError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'ResponseContentLengthMismatchError'\n    this.message = message || 'Response body length does not match content-length header'\n    this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kResponseContentLengthMismatchError] === true\n  }\n\n  get [kResponseContentLengthMismatchError] () {\n    return true\n  }\n}\n\nconst kClientDestroyedError = Symbol.for('undici.error.UND_ERR_DESTROYED')\nclass ClientDestroyedError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'ClientDestroyedError'\n    this.message = message || 'The client is destroyed'\n    this.code = 'UND_ERR_DESTROYED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kClientDestroyedError] === true\n  }\n\n  get [kClientDestroyedError] () {\n    return true\n  }\n}\n\nconst kClientClosedError = Symbol.for('undici.error.UND_ERR_CLOSED')\nclass ClientClosedError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'ClientClosedError'\n    this.message = message || 'The client is closed'\n    this.code = 'UND_ERR_CLOSED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kClientClosedError] === true\n  }\n\n  get [kClientClosedError] () {\n    return true\n  }\n}\n\nconst kSocketError = Symbol.for('undici.error.UND_ERR_SOCKET')\nclass SocketError extends UndiciError {\n  constructor (message, socket) {\n    super(message)\n    this.name = 'SocketError'\n    this.message = message || 'Socket error'\n    this.code = 'UND_ERR_SOCKET'\n    this.socket = socket\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kSocketError] === true\n  }\n\n  get [kSocketError] () {\n    return true\n  }\n}\n\nconst kNotSupportedError = Symbol.for('undici.error.UND_ERR_NOT_SUPPORTED')\nclass NotSupportedError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'NotSupportedError'\n    this.message = message || 'Not supported error'\n    this.code = 'UND_ERR_NOT_SUPPORTED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kNotSupportedError] === true\n  }\n\n  get [kNotSupportedError] () {\n    return true\n  }\n}\n\nconst kBalancedPoolMissingUpstreamError = Symbol.for('undici.error.UND_ERR_BPL_MISSING_UPSTREAM')\nclass BalancedPoolMissingUpstreamError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'MissingUpstreamError'\n    this.message = message || 'No upstream has been added to the BalancedPool'\n    this.code = 'UND_ERR_BPL_MISSING_UPSTREAM'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kBalancedPoolMissingUpstreamError] === true\n  }\n\n  get [kBalancedPoolMissingUpstreamError] () {\n    return true\n  }\n}\n\nconst kHTTPParserError = Symbol.for('undici.error.UND_ERR_HTTP_PARSER')\nclass HTTPParserError extends Error {\n  constructor (message, code, data) {\n    super(message)\n    this.name = 'HTTPParserError'\n    this.code = code ? `HPE_${code}` : undefined\n    this.data = data ? data.toString() : undefined\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kHTTPParserError] === true\n  }\n\n  get [kHTTPParserError] () {\n    return true\n  }\n}\n\nconst kResponseExceededMaxSizeError = Symbol.for('undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE')\nclass ResponseExceededMaxSizeError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'ResponseExceededMaxSizeError'\n    this.message = message || 'Response content exceeded max size'\n    this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kResponseExceededMaxSizeError] === true\n  }\n\n  get [kResponseExceededMaxSizeError] () {\n    return true\n  }\n}\n\nconst kRequestRetryError = Symbol.for('undici.error.UND_ERR_REQ_RETRY')\nclass RequestRetryError extends UndiciError {\n  constructor (message, code, { headers, data }) {\n    super(message)\n    this.name = 'RequestRetryError'\n    this.message = message || 'Request retry error'\n    this.code = 'UND_ERR_REQ_RETRY'\n    this.statusCode = code\n    this.data = data\n    this.headers = headers\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kRequestRetryError] === true\n  }\n\n  get [kRequestRetryError] () {\n    return true\n  }\n}\n\nconst kResponseError = Symbol.for('undici.error.UND_ERR_RESPONSE')\nclass ResponseError extends UndiciError {\n  constructor (message, code, { headers, body }) {\n    super(message)\n    this.name = 'ResponseError'\n    this.message = message || 'Response error'\n    this.code = 'UND_ERR_RESPONSE'\n    this.statusCode = code\n    this.body = body\n    this.headers = headers\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kResponseError] === true\n  }\n\n  get [kResponseError] () {\n    return true\n  }\n}\n\nconst kSecureProxyConnectionError = Symbol.for('undici.error.UND_ERR_PRX_TLS')\nclass SecureProxyConnectionError extends UndiciError {\n  constructor (cause, message, options = {}) {\n    super(message, { cause, ...options })\n    this.name = 'SecureProxyConnectionError'\n    this.message = message || 'Secure Proxy Connection failed'\n    this.code = 'UND_ERR_PRX_TLS'\n    this.cause = cause\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kSecureProxyConnectionError] === true\n  }\n\n  get [kSecureProxyConnectionError] () {\n    return true\n  }\n}\n\nconst kMaxOriginsReachedError = Symbol.for('undici.error.UND_ERR_MAX_ORIGINS_REACHED')\nclass MaxOriginsReachedError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'MaxOriginsReachedError'\n    this.message = message || 'Maximum allowed origins reached'\n    this.code = 'UND_ERR_MAX_ORIGINS_REACHED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kMaxOriginsReachedError] === true\n  }\n\n  get [kMaxOriginsReachedError] () {\n    return true\n  }\n}\n\nclass Socks5ProxyError extends UndiciError {\n  constructor (message, code) {\n    super(message)\n    this.name = 'Socks5ProxyError'\n    this.message = message || 'SOCKS5 proxy error'\n    this.code = code || 'UND_ERR_SOCKS5'\n  }\n}\n\nconst kMessageSizeExceededError = Symbol.for('undici.error.UND_ERR_WS_MESSAGE_SIZE_EXCEEDED')\nclass MessageSizeExceededError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'MessageSizeExceededError'\n    this.message = message || 'Max decompressed message size exceeded'\n    this.code = 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kMessageSizeExceededError] === true\n  }\n\n  get [kMessageSizeExceededError] () {\n    return true\n  }\n}\n\nmodule.exports = {\n  AbortError,\n  HTTPParserError,\n  UndiciError,\n  HeadersTimeoutError,\n  HeadersOverflowError,\n  BodyTimeoutError,\n  RequestContentLengthMismatchError,\n  ConnectTimeoutError,\n  InvalidArgumentError,\n  InvalidReturnValueError,\n  RequestAbortedError,\n  ClientDestroyedError,\n  ClientClosedError,\n  InformationalError,\n  SocketError,\n  NotSupportedError,\n  ResponseContentLengthMismatchError,\n  BalancedPoolMissingUpstreamError,\n  ResponseExceededMaxSizeError,\n  RequestRetryError,\n  ResponseError,\n  SecureProxyConnectionError,\n  MaxOriginsReachedError,\n  Socks5ProxyError,\n  MessageSizeExceededError\n}\n"
  },
  {
    "path": "lib/core/request.js",
    "content": "'use strict'\n\nconst {\n  InvalidArgumentError,\n  NotSupportedError\n} = require('./errors')\nconst assert = require('node:assert')\nconst {\n  isValidHTTPToken,\n  isValidHeaderValue,\n  isStream,\n  destroy,\n  isBuffer,\n  isFormDataLike,\n  isIterable,\n  hasSafeIterator,\n  isBlobLike,\n  serializePathWithQuery,\n  assertRequestHandler,\n  getServerName,\n  normalizedMethodRecords,\n  getProtocolFromUrlString\n} = require('./util')\nconst { channels } = require('./diagnostics.js')\nconst { headerNameLowerCasedRecord } = require('./constants')\n\n// Verifies that a given path is valid does not contain control chars \\x00 to \\x20\nconst invalidPathRegex = /[^\\u0021-\\u00ff]/\n\nconst kHandler = Symbol('handler')\n\nclass Request {\n  constructor (origin, {\n    path,\n    method,\n    body,\n    headers,\n    query,\n    idempotent,\n    blocking,\n    upgrade,\n    headersTimeout,\n    bodyTimeout,\n    reset,\n    expectContinue,\n    servername,\n    throwOnError,\n    maxRedirections,\n    typeOfService\n  }, handler) {\n    if (typeof path !== 'string') {\n      throw new InvalidArgumentError('path must be a string')\n    } else if (\n      path[0] !== '/' &&\n      !(path.startsWith('http://') || path.startsWith('https://')) &&\n      method !== 'CONNECT'\n    ) {\n      throw new InvalidArgumentError('path must be an absolute URL or start with a slash')\n    } else if (invalidPathRegex.test(path)) {\n      throw new InvalidArgumentError('invalid request path')\n    }\n\n    if (typeof method !== 'string') {\n      throw new InvalidArgumentError('method must be a string')\n    } else if (normalizedMethodRecords[method] === undefined && !isValidHTTPToken(method)) {\n      throw new InvalidArgumentError('invalid request method')\n    }\n\n    if (upgrade && typeof upgrade !== 'string') {\n      throw new InvalidArgumentError('upgrade must be a string')\n    }\n\n    if (upgrade && !isValidHeaderValue(upgrade)) {\n      throw new InvalidArgumentError('invalid upgrade header')\n    }\n\n    if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {\n      throw new InvalidArgumentError('invalid headersTimeout')\n    }\n\n    if (bodyTimeout != null && (!Number.isFinite(bodyTimeout) || bodyTimeout < 0)) {\n      throw new InvalidArgumentError('invalid bodyTimeout')\n    }\n\n    if (reset != null && typeof reset !== 'boolean') {\n      throw new InvalidArgumentError('invalid reset')\n    }\n\n    if (expectContinue != null && typeof expectContinue !== 'boolean') {\n      throw new InvalidArgumentError('invalid expectContinue')\n    }\n\n    if (throwOnError != null) {\n      throw new InvalidArgumentError('invalid throwOnError')\n    }\n\n    if (maxRedirections != null && maxRedirections !== 0) {\n      throw new InvalidArgumentError('maxRedirections is not supported, use the redirect interceptor')\n    }\n\n    if (typeOfService != null && (!Number.isInteger(typeOfService) || typeOfService < 0 || typeOfService > 255)) {\n      throw new InvalidArgumentError('typeOfService must be an integer between 0 and 255')\n    }\n\n    this.headersTimeout = headersTimeout\n\n    this.bodyTimeout = bodyTimeout\n\n    this.method = method\n\n    this.typeOfService = typeOfService ?? 0\n\n    this.abort = null\n\n    if (body == null) {\n      this.body = null\n    } else if (isStream(body)) {\n      this.body = body\n\n      const rState = this.body._readableState\n      if (!rState || !rState.autoDestroy) {\n        this.endHandler = function autoDestroy () {\n          destroy(this)\n        }\n        this.body.on('end', this.endHandler)\n      }\n\n      this.errorHandler = err => {\n        if (this.abort) {\n          this.abort(err)\n        } else {\n          this.error = err\n        }\n      }\n      this.body.on('error', this.errorHandler)\n    } else if (isBuffer(body)) {\n      this.body = body.byteLength ? body : null\n    } else if (ArrayBuffer.isView(body)) {\n      this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null\n    } else if (body instanceof ArrayBuffer) {\n      this.body = body.byteLength ? Buffer.from(body) : null\n    } else if (typeof body === 'string') {\n      this.body = body.length ? Buffer.from(body) : null\n    } else if (isFormDataLike(body) || isIterable(body) || isBlobLike(body)) {\n      this.body = body\n    } else {\n      throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')\n    }\n\n    this.completed = false\n    this.aborted = false\n\n    this.upgrade = upgrade || null\n\n    this.path = query ? serializePathWithQuery(path, query) : path\n\n    // TODO: shall we maybe standardize it to an URL object?\n    this.origin = origin\n\n    this.protocol = getProtocolFromUrlString(origin)\n\n    this.idempotent = idempotent == null\n      ? method === 'HEAD' || method === 'GET'\n      : idempotent\n\n    this.blocking = blocking ?? this.method !== 'HEAD'\n\n    this.reset = reset == null ? null : reset\n\n    this.host = null\n\n    this.contentLength = null\n\n    this.contentType = null\n\n    this.headers = []\n\n    // Only for H2\n    this.expectContinue = expectContinue != null ? expectContinue : false\n\n    if (Array.isArray(headers)) {\n      if (headers.length % 2 !== 0) {\n        throw new InvalidArgumentError('headers array must be even')\n      }\n      for (let i = 0; i < headers.length; i += 2) {\n        processHeader(this, headers[i], headers[i + 1])\n      }\n    } else if (headers && typeof headers === 'object') {\n      if (hasSafeIterator(headers)) {\n        for (const header of headers) {\n          if (!Array.isArray(header) || header.length !== 2) {\n            throw new InvalidArgumentError('headers must be in key-value pair format')\n          }\n          processHeader(this, header[0], header[1])\n        }\n      } else {\n        const keys = Object.keys(headers)\n        for (let i = 0; i < keys.length; ++i) {\n          processHeader(this, keys[i], headers[keys[i]])\n        }\n      }\n    } else if (headers != null) {\n      throw new InvalidArgumentError('headers must be an object or an array')\n    }\n\n    assertRequestHandler(handler, method, upgrade)\n\n    this.servername = servername || getServerName(this.host) || null\n\n    this[kHandler] = handler\n\n    if (channels.create.hasSubscribers) {\n      channels.create.publish({ request: this })\n    }\n  }\n\n  onBodySent (chunk) {\n    if (channels.bodyChunkSent.hasSubscribers) {\n      channels.bodyChunkSent.publish({ request: this, chunk })\n    }\n    if (this[kHandler].onBodySent) {\n      try {\n        return this[kHandler].onBodySent(chunk)\n      } catch (err) {\n        this.abort(err)\n      }\n    }\n  }\n\n  onRequestSent () {\n    if (channels.bodySent.hasSubscribers) {\n      channels.bodySent.publish({ request: this })\n    }\n\n    if (this[kHandler].onRequestSent) {\n      try {\n        return this[kHandler].onRequestSent()\n      } catch (err) {\n        this.abort(err)\n      }\n    }\n  }\n\n  onConnect (abort) {\n    assert(!this.aborted)\n    assert(!this.completed)\n\n    if (this.error) {\n      abort(this.error)\n    } else {\n      this.abort = abort\n      return this[kHandler].onConnect(abort)\n    }\n  }\n\n  onResponseStarted () {\n    return this[kHandler].onResponseStarted?.()\n  }\n\n  onHeaders (statusCode, headers, resume, statusText) {\n    assert(!this.aborted)\n    assert(!this.completed)\n\n    if (channels.headers.hasSubscribers) {\n      channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })\n    }\n\n    try {\n      return this[kHandler].onHeaders(statusCode, headers, resume, statusText)\n    } catch (err) {\n      this.abort(err)\n    }\n  }\n\n  onData (chunk) {\n    assert(!this.aborted)\n    assert(!this.completed)\n\n    if (channels.bodyChunkReceived.hasSubscribers) {\n      channels.bodyChunkReceived.publish({ request: this, chunk })\n    }\n    try {\n      return this[kHandler].onData(chunk)\n    } catch (err) {\n      this.abort(err)\n      return false\n    }\n  }\n\n  onUpgrade (statusCode, headers, socket) {\n    assert(!this.aborted)\n    assert(!this.completed)\n\n    return this[kHandler].onUpgrade(statusCode, headers, socket)\n  }\n\n  onComplete (trailers) {\n    this.onFinally()\n\n    assert(!this.aborted)\n    assert(!this.completed)\n\n    this.completed = true\n    if (channels.trailers.hasSubscribers) {\n      channels.trailers.publish({ request: this, trailers })\n    }\n\n    try {\n      return this[kHandler].onComplete(trailers)\n    } catch (err) {\n      // TODO (fix): This might be a bad idea?\n      this.onError(err)\n    }\n  }\n\n  onError (error) {\n    this.onFinally()\n\n    if (channels.error.hasSubscribers) {\n      channels.error.publish({ request: this, error })\n    }\n\n    if (this.aborted) {\n      return\n    }\n    this.aborted = true\n\n    return this[kHandler].onError(error)\n  }\n\n  onFinally () {\n    if (this.errorHandler) {\n      this.body.off('error', this.errorHandler)\n      this.errorHandler = null\n    }\n\n    if (this.endHandler) {\n      this.body.off('end', this.endHandler)\n      this.endHandler = null\n    }\n  }\n\n  addHeader (key, value) {\n    processHeader(this, key, value)\n    return this\n  }\n}\n\nfunction processHeader (request, key, val) {\n  if (val && (typeof val === 'object' && !Array.isArray(val))) {\n    throw new InvalidArgumentError(`invalid ${key} header`)\n  } else if (val === undefined) {\n    return\n  }\n\n  let headerName = headerNameLowerCasedRecord[key]\n\n  if (headerName === undefined) {\n    headerName = key.toLowerCase()\n    if (headerNameLowerCasedRecord[headerName] === undefined && !isValidHTTPToken(headerName)) {\n      throw new InvalidArgumentError('invalid header key')\n    }\n  }\n\n  if (Array.isArray(val)) {\n    const arr = []\n    for (let i = 0; i < val.length; i++) {\n      if (typeof val[i] === 'string') {\n        if (!isValidHeaderValue(val[i])) {\n          throw new InvalidArgumentError(`invalid ${key} header`)\n        }\n        arr.push(val[i])\n      } else if (val[i] === null) {\n        arr.push('')\n      } else if (typeof val[i] === 'object') {\n        throw new InvalidArgumentError(`invalid ${key} header`)\n      } else {\n        arr.push(`${val[i]}`)\n      }\n    }\n    val = arr\n  } else if (typeof val === 'string') {\n    if (!isValidHeaderValue(val)) {\n      throw new InvalidArgumentError(`invalid ${key} header`)\n    }\n  } else if (val === null) {\n    val = ''\n  } else {\n    val = `${val}`\n  }\n\n  if (headerName === 'host') {\n    if (request.host !== null) {\n      throw new InvalidArgumentError('duplicate host header')\n    }\n    if (typeof val !== 'string') {\n      throw new InvalidArgumentError('invalid host header')\n    }\n    // Consumed by Client\n    request.host = val\n  } else if (headerName === 'content-length') {\n    if (request.contentLength !== null) {\n      throw new InvalidArgumentError('duplicate content-length header')\n    }\n    request.contentLength = parseInt(val, 10)\n    if (!Number.isFinite(request.contentLength)) {\n      throw new InvalidArgumentError('invalid content-length header')\n    }\n  } else if (request.contentType === null && headerName === 'content-type') {\n    request.contentType = val\n    request.headers.push(key, val)\n  } else if (headerName === 'transfer-encoding' || headerName === 'keep-alive' || headerName === 'upgrade') {\n    throw new InvalidArgumentError(`invalid ${headerName} header`)\n  } else if (headerName === 'connection') {\n    const value = typeof val === 'string' ? val.toLowerCase() : null\n    if (value !== 'close' && value !== 'keep-alive') {\n      throw new InvalidArgumentError('invalid connection header')\n    }\n\n    if (value === 'close') {\n      request.reset = true\n    }\n  } else if (headerName === 'expect') {\n    throw new NotSupportedError('expect header not supported')\n  } else {\n    request.headers.push(key, val)\n  }\n}\n\nmodule.exports = Request\n"
  },
  {
    "path": "lib/core/socks5-client.js",
    "content": "'use strict'\n\nconst { EventEmitter } = require('node:events')\nconst { Buffer } = require('node:buffer')\nconst { InvalidArgumentError, Socks5ProxyError } = require('./errors')\nconst { debuglog } = require('node:util')\nconst { parseAddress } = require('./socks5-utils')\n\nconst debug = debuglog('undici:socks5')\n\n// SOCKS5 constants\nconst SOCKS_VERSION = 0x05\n\n// Authentication methods\nconst AUTH_METHODS = {\n  NO_AUTH: 0x00,\n  GSSAPI: 0x01,\n  USERNAME_PASSWORD: 0x02,\n  NO_ACCEPTABLE: 0xFF\n}\n\n// SOCKS5 commands\nconst COMMANDS = {\n  CONNECT: 0x01,\n  BIND: 0x02,\n  UDP_ASSOCIATE: 0x03\n}\n\n// Address types\nconst ADDRESS_TYPES = {\n  IPV4: 0x01,\n  DOMAIN: 0x03,\n  IPV6: 0x04\n}\n\n// Reply codes\nconst REPLY_CODES = {\n  SUCCEEDED: 0x00,\n  GENERAL_FAILURE: 0x01,\n  CONNECTION_NOT_ALLOWED: 0x02,\n  NETWORK_UNREACHABLE: 0x03,\n  HOST_UNREACHABLE: 0x04,\n  CONNECTION_REFUSED: 0x05,\n  TTL_EXPIRED: 0x06,\n  COMMAND_NOT_SUPPORTED: 0x07,\n  ADDRESS_TYPE_NOT_SUPPORTED: 0x08\n}\n\n// State machine states\nconst STATES = {\n  INITIAL: 'initial',\n  HANDSHAKING: 'handshaking',\n  AUTHENTICATING: 'authenticating',\n  CONNECTING: 'connecting',\n  CONNECTED: 'connected',\n  ERROR: 'error',\n  CLOSED: 'closed'\n}\n\n/**\n * SOCKS5 client implementation\n * Handles SOCKS5 protocol negotiation and connection establishment\n */\nclass Socks5Client extends EventEmitter {\n  constructor (socket, options = {}) {\n    super()\n\n    if (!socket) {\n      throw new InvalidArgumentError('socket is required')\n    }\n\n    this.socket = socket\n    this.options = options\n    this.state = STATES.INITIAL\n    this.buffer = Buffer.alloc(0)\n\n    // Authentication settings\n    this.authMethods = []\n    if (options.username && options.password) {\n      this.authMethods.push(AUTH_METHODS.USERNAME_PASSWORD)\n    }\n    this.authMethods.push(AUTH_METHODS.NO_AUTH)\n\n    // Socket event handlers\n    this.socket.on('data', this.onData.bind(this))\n    this.socket.on('error', this.onError.bind(this))\n    this.socket.on('close', this.onClose.bind(this))\n  }\n\n  /**\n   * Handle incoming data from the socket\n   */\n  onData (data) {\n    debug('received data', data.length, 'bytes in state', this.state)\n    this.buffer = Buffer.concat([this.buffer, data])\n\n    try {\n      switch (this.state) {\n        case STATES.HANDSHAKING:\n          this.handleHandshakeResponse()\n          break\n        case STATES.AUTHENTICATING:\n          this.handleAuthResponse()\n          break\n        case STATES.CONNECTING:\n          this.handleConnectResponse()\n          break\n      }\n    } catch (err) {\n      this.onError(err)\n    }\n  }\n\n  /**\n   * Handle socket errors\n   */\n  onError (err) {\n    debug('socket error', err)\n    this.state = STATES.ERROR\n    this.emit('error', err)\n    this.destroy()\n  }\n\n  /**\n   * Handle socket close\n   */\n  onClose () {\n    debug('socket closed')\n    this.state = STATES.CLOSED\n    this.emit('close')\n  }\n\n  /**\n   * Destroy the client and underlying socket\n   */\n  destroy () {\n    if (this.socket && !this.socket.destroyed) {\n      this.socket.destroy()\n    }\n  }\n\n  /**\n   * Start the SOCKS5 handshake\n   */\n  handshake () {\n    if (this.state !== STATES.INITIAL) {\n      throw new InvalidArgumentError('Handshake already started')\n    }\n\n    debug('starting handshake with', this.authMethods.length, 'auth methods')\n    this.state = STATES.HANDSHAKING\n\n    // Build handshake request\n    // +----+----------+----------+\n    // |VER | NMETHODS | METHODS  |\n    // +----+----------+----------+\n    // | 1  |    1     | 1 to 255 |\n    // +----+----------+----------+\n    const request = Buffer.alloc(2 + this.authMethods.length)\n    request[0] = SOCKS_VERSION\n    request[1] = this.authMethods.length\n    this.authMethods.forEach((method, i) => {\n      request[2 + i] = method\n    })\n\n    this.socket.write(request)\n  }\n\n  /**\n   * Handle handshake response from server\n   */\n  handleHandshakeResponse () {\n    if (this.buffer.length < 2) {\n      return // Not enough data yet\n    }\n\n    const version = this.buffer[0]\n    const method = this.buffer[1]\n\n    if (version !== SOCKS_VERSION) {\n      throw new Socks5ProxyError(`Invalid SOCKS version: ${version}`, 'UND_ERR_SOCKS5_VERSION')\n    }\n\n    if (method === AUTH_METHODS.NO_ACCEPTABLE) {\n      throw new Socks5ProxyError('No acceptable authentication method', 'UND_ERR_SOCKS5_AUTH_REJECTED')\n    }\n\n    this.buffer = this.buffer.subarray(2)\n    debug('server selected auth method', method)\n\n    if (method === AUTH_METHODS.NO_AUTH) {\n      this.emit('authenticated')\n    } else if (method === AUTH_METHODS.USERNAME_PASSWORD) {\n      this.state = STATES.AUTHENTICATING\n      this.sendAuthRequest()\n    } else {\n      throw new Socks5ProxyError(`Unsupported authentication method: ${method}`, 'UND_ERR_SOCKS5_AUTH_METHOD')\n    }\n  }\n\n  /**\n   * Send username/password authentication request\n   */\n  sendAuthRequest () {\n    const { username, password } = this.options\n\n    if (!username || !password) {\n      throw new InvalidArgumentError('Username and password required for authentication')\n    }\n\n    debug('sending username/password auth')\n\n    // Username/Password authentication request (RFC 1929)\n    // +----+------+----------+------+----------+\n    // |VER | ULEN |  UNAME   | PLEN |  PASSWD  |\n    // +----+------+----------+------+----------+\n    // | 1  |  1   | 1 to 255 |  1   | 1 to 255 |\n    // +----+------+----------+------+----------+\n    const usernameBuffer = Buffer.from(username)\n    const passwordBuffer = Buffer.from(password)\n\n    if (usernameBuffer.length > 255 || passwordBuffer.length > 255) {\n      throw new InvalidArgumentError('Username or password too long')\n    }\n\n    const request = Buffer.alloc(3 + usernameBuffer.length + passwordBuffer.length)\n    request[0] = 0x01 // Sub-negotiation version\n    request[1] = usernameBuffer.length\n    usernameBuffer.copy(request, 2)\n    request[2 + usernameBuffer.length] = passwordBuffer.length\n    passwordBuffer.copy(request, 3 + usernameBuffer.length)\n\n    this.socket.write(request)\n  }\n\n  /**\n   * Handle authentication response\n   */\n  handleAuthResponse () {\n    if (this.buffer.length < 2) {\n      return // Not enough data yet\n    }\n\n    const version = this.buffer[0]\n    const status = this.buffer[1]\n\n    if (version !== 0x01) {\n      throw new Socks5ProxyError(`Invalid auth sub-negotiation version: ${version}`, 'UND_ERR_SOCKS5_AUTH_VERSION')\n    }\n\n    if (status !== 0x00) {\n      throw new Socks5ProxyError('Authentication failed', 'UND_ERR_SOCKS5_AUTH_FAILED')\n    }\n\n    this.buffer = this.buffer.subarray(2)\n    debug('authentication successful')\n    this.emit('authenticated')\n  }\n\n  /**\n   * Send CONNECT command\n   * @param {string} address - Target address (IP or domain)\n   * @param {number} port - Target port\n   */\n  connect (address, port) {\n    if (this.state === STATES.CONNECTED) {\n      throw new InvalidArgumentError('Already connected')\n    }\n\n    debug('connecting to', address, port)\n    this.state = STATES.CONNECTING\n\n    const request = this.buildConnectRequest(COMMANDS.CONNECT, address, port)\n    this.socket.write(request)\n  }\n\n  /**\n   * Build a SOCKS5 request\n   */\n  buildConnectRequest (command, address, port) {\n    // Parse address to determine type and buffer\n    const { type: addressType, buffer: addressBuffer } = parseAddress(address)\n\n    // Build request\n    // +----+-----+-------+------+----------+----------+\n    // |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |\n    // +----+-----+-------+------+----------+----------+\n    // | 1  |  1  | X'00' |  1   | Variable |    2     |\n    // +----+-----+-------+------+----------+----------+\n    const request = Buffer.alloc(4 + addressBuffer.length + 2)\n    request[0] = SOCKS_VERSION\n    request[1] = command\n    request[2] = 0x00 // Reserved\n    request[3] = addressType\n    addressBuffer.copy(request, 4)\n    request.writeUInt16BE(port, 4 + addressBuffer.length)\n\n    return request\n  }\n\n  /**\n   * Handle CONNECT response\n   */\n  handleConnectResponse () {\n    if (this.buffer.length < 4) {\n      return // Not enough data for header\n    }\n\n    const version = this.buffer[0]\n    const reply = this.buffer[1]\n    const addressType = this.buffer[3]\n\n    if (version !== SOCKS_VERSION) {\n      throw new Socks5ProxyError(`Invalid SOCKS version in reply: ${version}`, 'UND_ERR_SOCKS5_REPLY_VERSION')\n    }\n\n    // Calculate the expected response length\n    let responseLength = 4 // VER + REP + RSV + ATYP\n    if (addressType === ADDRESS_TYPES.IPV4) {\n      responseLength += 4 + 2 // IPv4 + port\n    } else if (addressType === ADDRESS_TYPES.DOMAIN) {\n      if (this.buffer.length < 5) {\n        return // Need domain length byte\n      }\n      responseLength += 1 + this.buffer[4] + 2 // length byte + domain + port\n    } else if (addressType === ADDRESS_TYPES.IPV6) {\n      responseLength += 16 + 2 // IPv6 + port\n    } else {\n      throw new Socks5ProxyError(`Invalid address type in reply: ${addressType}`, 'UND_ERR_SOCKS5_ADDR_TYPE')\n    }\n\n    if (this.buffer.length < responseLength) {\n      return // Not enough data for full response\n    }\n\n    if (reply !== REPLY_CODES.SUCCEEDED) {\n      const errorMessage = this.getReplyErrorMessage(reply)\n      throw new Socks5ProxyError(`SOCKS5 connection failed: ${errorMessage}`, `UND_ERR_SOCKS5_REPLY_${reply}`)\n    }\n\n    // Parse bound address and port\n    let boundAddress\n    let offset = 4\n\n    if (addressType === ADDRESS_TYPES.IPV4) {\n      boundAddress = Array.from(this.buffer.subarray(offset, offset + 4)).join('.')\n      offset += 4\n    } else if (addressType === ADDRESS_TYPES.DOMAIN) {\n      const domainLength = this.buffer[offset]\n      offset += 1\n      boundAddress = this.buffer.subarray(offset, offset + domainLength).toString()\n      offset += domainLength\n    } else if (addressType === ADDRESS_TYPES.IPV6) {\n      // Parse IPv6 address from 16-byte buffer\n      const parts = []\n      for (let i = 0; i < 8; i++) {\n        const value = this.buffer.readUInt16BE(offset + i * 2)\n        parts.push(value.toString(16))\n      }\n      boundAddress = parts.join(':')\n      offset += 16\n    }\n\n    const boundPort = this.buffer.readUInt16BE(offset)\n\n    this.buffer = this.buffer.subarray(responseLength)\n    this.state = STATES.CONNECTED\n\n    debug('connected, bound address:', boundAddress, 'port:', boundPort)\n    this.emit('connected', { address: boundAddress, port: boundPort })\n  }\n\n  /**\n   * Get human-readable error message for reply code\n   */\n  getReplyErrorMessage (reply) {\n    switch (reply) {\n      case REPLY_CODES.GENERAL_FAILURE:\n        return 'General SOCKS server failure'\n      case REPLY_CODES.CONNECTION_NOT_ALLOWED:\n        return 'Connection not allowed by ruleset'\n      case REPLY_CODES.NETWORK_UNREACHABLE:\n        return 'Network unreachable'\n      case REPLY_CODES.HOST_UNREACHABLE:\n        return 'Host unreachable'\n      case REPLY_CODES.CONNECTION_REFUSED:\n        return 'Connection refused'\n      case REPLY_CODES.TTL_EXPIRED:\n        return 'TTL expired'\n      case REPLY_CODES.COMMAND_NOT_SUPPORTED:\n        return 'Command not supported'\n      case REPLY_CODES.ADDRESS_TYPE_NOT_SUPPORTED:\n        return 'Address type not supported'\n      default:\n        return `Unknown error code: ${reply}`\n    }\n  }\n}\n\nmodule.exports = {\n  Socks5Client,\n  AUTH_METHODS,\n  COMMANDS,\n  ADDRESS_TYPES,\n  REPLY_CODES,\n  STATES\n}\n"
  },
  {
    "path": "lib/core/socks5-utils.js",
    "content": "'use strict'\n\nconst { Buffer } = require('node:buffer')\nconst net = require('node:net')\nconst { InvalidArgumentError } = require('./errors')\n\n/**\n * Parse an address and determine its type\n * @param {string} address - The address to parse\n * @returns {{type: number, buffer: Buffer}} Address type and buffer\n */\nfunction parseAddress (address) {\n  // Check if it's an IPv4 address\n  if (net.isIPv4(address)) {\n    const parts = address.split('.').map(Number)\n    return {\n      type: 0x01, // IPv4\n      buffer: Buffer.from(parts)\n    }\n  }\n\n  // Check if it's an IPv6 address\n  if (net.isIPv6(address)) {\n    return {\n      type: 0x04, // IPv6\n      buffer: parseIPv6(address)\n    }\n  }\n\n  // Otherwise, treat as domain name\n  const domainBuffer = Buffer.from(address, 'utf8')\n  if (domainBuffer.length > 255) {\n    throw new InvalidArgumentError('Domain name too long (max 255 bytes)')\n  }\n\n  return {\n    type: 0x03, // Domain\n    buffer: Buffer.concat([Buffer.from([domainBuffer.length]), domainBuffer])\n  }\n}\n\n/**\n * Parse IPv6 address to buffer\n * @param {string} address - IPv6 address string\n * @returns {Buffer} 16-byte buffer\n */\nfunction parseIPv6 (address) {\n  const buffer = Buffer.alloc(16)\n  const parts = address.split(':')\n  let partIndex = 0\n  let bufferIndex = 0\n\n  // Handle compressed notation (::)\n  const doubleColonIndex = address.indexOf('::')\n  if (doubleColonIndex !== -1) {\n    // Count non-empty parts\n    const nonEmptyParts = parts.filter(p => p.length > 0).length\n    const skipParts = 8 - nonEmptyParts\n\n    for (let i = 0; i < parts.length; i++) {\n      if (parts[i] === '' && i === doubleColonIndex / 3) {\n        // Skip empty parts for ::\n        bufferIndex += skipParts * 2\n      } else if (parts[i] !== '') {\n        const value = parseInt(parts[i], 16)\n        buffer.writeUInt16BE(value, bufferIndex)\n        bufferIndex += 2\n      }\n    }\n  } else {\n    // No compression, parse normally\n    for (const part of parts) {\n      if (part === '') continue\n      const value = parseInt(part, 16)\n      buffer.writeUInt16BE(value, partIndex * 2)\n      partIndex++\n    }\n  }\n\n  return buffer\n}\n\n/**\n * Build a SOCKS5 address buffer\n * @param {number} type - Address type (1=IPv4, 3=Domain, 4=IPv6)\n * @param {Buffer} addressBuffer - The address data\n * @param {number} port - Port number\n * @returns {Buffer} Complete address buffer including type, address, and port\n */\nfunction buildAddressBuffer (type, addressBuffer, port) {\n  const portBuffer = Buffer.allocUnsafe(2)\n  portBuffer.writeUInt16BE(port, 0)\n\n  return Buffer.concat([\n    Buffer.from([type]),\n    addressBuffer,\n    portBuffer\n  ])\n}\n\n/**\n * Parse address from SOCKS5 response\n * @param {Buffer} buffer - Buffer containing the address\n * @param {number} offset - Starting offset in buffer\n * @returns {{address: string, port: number, bytesRead: number}}\n */\nfunction parseResponseAddress (buffer, offset = 0) {\n  if (buffer.length < offset + 1) {\n    throw new InvalidArgumentError('Buffer too small to contain address type')\n  }\n\n  const addressType = buffer[offset]\n  let address\n  let currentOffset = offset + 1\n\n  switch (addressType) {\n    case 0x01: { // IPv4\n      if (buffer.length < currentOffset + 6) {\n        throw new InvalidArgumentError('Buffer too small for IPv4 address')\n      }\n      address = Array.from(buffer.subarray(currentOffset, currentOffset + 4)).join('.')\n      currentOffset += 4\n      break\n    }\n\n    case 0x03: { // Domain\n      if (buffer.length < currentOffset + 1) {\n        throw new InvalidArgumentError('Buffer too small for domain length')\n      }\n      const domainLength = buffer[currentOffset]\n      currentOffset += 1\n\n      if (buffer.length < currentOffset + domainLength + 2) {\n        throw new InvalidArgumentError('Buffer too small for domain address')\n      }\n      address = buffer.subarray(currentOffset, currentOffset + domainLength).toString('utf8')\n      currentOffset += domainLength\n      break\n    }\n\n    case 0x04: { // IPv6\n      if (buffer.length < currentOffset + 18) {\n        throw new InvalidArgumentError('Buffer too small for IPv6 address')\n      }\n      // Convert buffer to IPv6 string\n      const parts = []\n      for (let i = 0; i < 8; i++) {\n        const value = buffer.readUInt16BE(currentOffset + i * 2)\n        parts.push(value.toString(16))\n      }\n      address = parts.join(':')\n      currentOffset += 16\n      break\n    }\n\n    default:\n      throw new InvalidArgumentError(`Invalid address type: ${addressType}`)\n  }\n\n  // Parse port\n  if (buffer.length < currentOffset + 2) {\n    throw new InvalidArgumentError('Buffer too small for port')\n  }\n  const port = buffer.readUInt16BE(currentOffset)\n  currentOffset += 2\n\n  return {\n    address,\n    port,\n    bytesRead: currentOffset - offset\n  }\n}\n\n/**\n * Create error for SOCKS5 reply code\n * @param {number} replyCode - SOCKS5 reply code\n * @returns {Error} Appropriate error object\n */\nfunction createReplyError (replyCode) {\n  const messages = {\n    0x01: 'General SOCKS server failure',\n    0x02: 'Connection not allowed by ruleset',\n    0x03: 'Network unreachable',\n    0x04: 'Host unreachable',\n    0x05: 'Connection refused',\n    0x06: 'TTL expired',\n    0x07: 'Command not supported',\n    0x08: 'Address type not supported'\n  }\n\n  const message = messages[replyCode] || `Unknown SOCKS5 error code: ${replyCode}`\n  const error = new Error(message)\n  error.code = `SOCKS5_${replyCode}`\n  return error\n}\n\nmodule.exports = {\n  parseAddress,\n  parseIPv6,\n  buildAddressBuffer,\n  parseResponseAddress,\n  createReplyError\n}\n"
  },
  {
    "path": "lib/core/symbols.js",
    "content": "'use strict'\n\nmodule.exports = {\n  kClose: Symbol('close'),\n  kDestroy: Symbol('destroy'),\n  kDispatch: Symbol('dispatch'),\n  kUrl: Symbol('url'),\n  kWriting: Symbol('writing'),\n  kResuming: Symbol('resuming'),\n  kQueue: Symbol('queue'),\n  kConnect: Symbol('connect'),\n  kConnecting: Symbol('connecting'),\n  kKeepAliveDefaultTimeout: Symbol('default keep alive timeout'),\n  kKeepAliveMaxTimeout: Symbol('max keep alive timeout'),\n  kKeepAliveTimeoutThreshold: Symbol('keep alive timeout threshold'),\n  kKeepAliveTimeoutValue: Symbol('keep alive timeout'),\n  kKeepAlive: Symbol('keep alive'),\n  kHeadersTimeout: Symbol('headers timeout'),\n  kBodyTimeout: Symbol('body timeout'),\n  kServerName: Symbol('server name'),\n  kLocalAddress: Symbol('local address'),\n  kHost: Symbol('host'),\n  kNoRef: Symbol('no ref'),\n  kBodyUsed: Symbol('used'),\n  kBody: Symbol('abstracted request body'),\n  kRunning: Symbol('running'),\n  kBlocking: Symbol('blocking'),\n  kPending: Symbol('pending'),\n  kSize: Symbol('size'),\n  kBusy: Symbol('busy'),\n  kQueued: Symbol('queued'),\n  kFree: Symbol('free'),\n  kConnected: Symbol('connected'),\n  kClosed: Symbol('closed'),\n  kNeedDrain: Symbol('need drain'),\n  kReset: Symbol('reset'),\n  kDestroyed: Symbol.for('nodejs.stream.destroyed'),\n  kResume: Symbol('resume'),\n  kOnError: Symbol('on error'),\n  kMaxHeadersSize: Symbol('max headers size'),\n  kRunningIdx: Symbol('running index'),\n  kPendingIdx: Symbol('pending index'),\n  kError: Symbol('error'),\n  kClients: Symbol('clients'),\n  kClient: Symbol('client'),\n  kParser: Symbol('parser'),\n  kOnDestroyed: Symbol('destroy callbacks'),\n  kPipelining: Symbol('pipelining'),\n  kSocket: Symbol('socket'),\n  kHostHeader: Symbol('host header'),\n  kConnector: Symbol('connector'),\n  kStrictContentLength: Symbol('strict content length'),\n  kMaxRedirections: Symbol('maxRedirections'),\n  kMaxRequests: Symbol('maxRequestsPerClient'),\n  kProxy: Symbol('proxy agent options'),\n  kCounter: Symbol('socket request counter'),\n  kMaxResponseSize: Symbol('max response size'),\n  kHTTP2Session: Symbol('http2Session'),\n  kHTTP2SessionState: Symbol('http2Session state'),\n  kRetryHandlerDefaultRetry: Symbol('retry agent default retry'),\n  kConstruct: Symbol('constructable'),\n  kListeners: Symbol('listeners'),\n  kHTTPContext: Symbol('http context'),\n  kMaxConcurrentStreams: Symbol('max concurrent streams'),\n  kHTTP2InitialWindowSize: Symbol('http2 initial window size'),\n  kHTTP2ConnectionWindowSize: Symbol('http2 connection window size'),\n  kEnableConnectProtocol: Symbol('http2session connect protocol'),\n  kRemoteSettings: Symbol('http2session remote settings'),\n  kHTTP2Stream: Symbol('http2session client stream'),\n  kPingInterval: Symbol('ping interval'),\n  kNoProxyAgent: Symbol('no proxy agent'),\n  kHttpProxyAgent: Symbol('http proxy agent'),\n  kHttpsProxyAgent: Symbol('https proxy agent'),\n  kSocks5ProxyAgent: Symbol('socks5 proxy agent')\n}\n"
  },
  {
    "path": "lib/core/tree.js",
    "content": "'use strict'\n\nconst {\n  wellknownHeaderNames,\n  headerNameLowerCasedRecord\n} = require('./constants')\n\nclass TstNode {\n  /** @type {any} */\n  value = null\n  /** @type {null | TstNode} */\n  left = null\n  /** @type {null | TstNode} */\n  middle = null\n  /** @type {null | TstNode} */\n  right = null\n  /** @type {number} */\n  code\n  /**\n   * @param {string} key\n   * @param {any} value\n   * @param {number} index\n   */\n  constructor (key, value, index) {\n    if (index === undefined || index >= key.length) {\n      throw new TypeError('Unreachable')\n    }\n    const code = this.code = key.charCodeAt(index)\n    // check code is ascii string\n    if (code > 0x7F) {\n      throw new TypeError('key must be ascii string')\n    }\n    if (key.length !== ++index) {\n      this.middle = new TstNode(key, value, index)\n    } else {\n      this.value = value\n    }\n  }\n\n  /**\n   * @param {string} key\n   * @param {any} value\n   * @returns {void}\n   */\n  add (key, value) {\n    const length = key.length\n    if (length === 0) {\n      throw new TypeError('Unreachable')\n    }\n    let index = 0\n    /**\n     * @type {TstNode}\n     */\n    let node = this\n    while (true) {\n      const code = key.charCodeAt(index)\n      // check code is ascii string\n      if (code > 0x7F) {\n        throw new TypeError('key must be ascii string')\n      }\n      if (node.code === code) {\n        if (length === ++index) {\n          node.value = value\n          break\n        } else if (node.middle !== null) {\n          node = node.middle\n        } else {\n          node.middle = new TstNode(key, value, index)\n          break\n        }\n      } else if (node.code < code) {\n        if (node.left !== null) {\n          node = node.left\n        } else {\n          node.left = new TstNode(key, value, index)\n          break\n        }\n      } else if (node.right !== null) {\n        node = node.right\n      } else {\n        node.right = new TstNode(key, value, index)\n        break\n      }\n    }\n  }\n\n  /**\n   * @param {Uint8Array} key\n   * @returns {TstNode | null}\n   */\n  search (key) {\n    const keylength = key.length\n    let index = 0\n    /**\n     * @type {TstNode|null}\n     */\n    let node = this\n    while (node !== null && index < keylength) {\n      let code = key[index]\n      // A-Z\n      // First check if it is bigger than 0x5a.\n      // Lowercase letters have higher char codes than uppercase ones.\n      // Also we assume that headers will mostly contain lowercase characters.\n      if (code <= 0x5a && code >= 0x41) {\n        // Lowercase for uppercase.\n        code |= 32\n      }\n      while (node !== null) {\n        if (code === node.code) {\n          if (keylength === ++index) {\n            // Returns Node since it is the last key.\n            return node\n          }\n          node = node.middle\n          break\n        }\n        node = node.code < code ? node.left : node.right\n      }\n    }\n    return null\n  }\n}\n\nclass TernarySearchTree {\n  /** @type {TstNode | null} */\n  node = null\n\n  /**\n   * @param {string} key\n   * @param {any} value\n   * @returns {void}\n   * */\n  insert (key, value) {\n    if (this.node === null) {\n      this.node = new TstNode(key, value, 0)\n    } else {\n      this.node.add(key, value)\n    }\n  }\n\n  /**\n   * @param {Uint8Array} key\n   * @returns {any}\n   */\n  lookup (key) {\n    return this.node?.search(key)?.value ?? null\n  }\n}\n\nconst tree = new TernarySearchTree()\n\nfor (let i = 0; i < wellknownHeaderNames.length; ++i) {\n  const key = headerNameLowerCasedRecord[wellknownHeaderNames[i]]\n  tree.insert(key, key)\n}\n\nmodule.exports = {\n  TernarySearchTree,\n  tree\n}\n"
  },
  {
    "path": "lib/core/util.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { kDestroyed, kBodyUsed, kListeners, kBody } = require('./symbols')\nconst { IncomingMessage } = require('node:http')\nconst stream = require('node:stream')\nconst net = require('node:net')\nconst { stringify } = require('node:querystring')\nconst { EventEmitter: EE } = require('node:events')\nconst timers = require('../util/timers')\nconst { InvalidArgumentError, ConnectTimeoutError } = require('./errors')\nconst { headerNameLowerCasedRecord } = require('./constants')\nconst { tree } = require('./tree')\n\nconst [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(v => Number(v))\n\nclass BodyAsyncIterable {\n  constructor (body) {\n    this[kBody] = body\n    this[kBodyUsed] = false\n  }\n\n  async * [Symbol.asyncIterator] () {\n    assert(!this[kBodyUsed], 'disturbed')\n    this[kBodyUsed] = true\n    yield * this[kBody]\n  }\n}\n\nfunction noop () {}\n\n/**\n * @param {*} body\n * @returns {*}\n */\nfunction wrapRequestBody (body) {\n  if (isStream(body)) {\n    // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp\n    // so that it can be dispatched again?\n    // TODO (fix): Do we need 100-expect support to provide a way to do this properly?\n    if (bodyLength(body) === 0) {\n      body\n        .on('data', function () {\n          assert(false)\n        })\n    }\n\n    if (typeof body.readableDidRead !== 'boolean') {\n      body[kBodyUsed] = false\n      EE.prototype.on.call(body, 'data', function () {\n        this[kBodyUsed] = true\n      })\n    }\n\n    return body\n  } else if (body && typeof body.pipeTo === 'function') {\n    // TODO (fix): We can't access ReadableStream internal state\n    // to determine whether or not it has been disturbed. This is just\n    // a workaround.\n    return new BodyAsyncIterable(body)\n  } else if (body && isFormDataLike(body)) {\n    return body\n  } else if (\n    body &&\n    typeof body !== 'string' &&\n    !ArrayBuffer.isView(body) &&\n    isIterable(body)\n  ) {\n    // TODO: Should we allow re-using iterable if !this.opts.idempotent\n    // or through some other flag?\n    return new BodyAsyncIterable(body)\n  } else {\n    return body\n  }\n}\n\n/**\n * @param {*} obj\n * @returns {obj is import('node:stream').Stream}\n */\nfunction isStream (obj) {\n  return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'\n}\n\n/**\n * @param {*} object\n * @returns {object is Blob}\n * based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)\n */\nfunction isBlobLike (object) {\n  if (object === null) {\n    return false\n  } else if (object instanceof Blob) {\n    return true\n  } else if (typeof object !== 'object') {\n    return false\n  } else {\n    const sTag = object[Symbol.toStringTag]\n\n    return (sTag === 'Blob' || sTag === 'File') && (\n      ('stream' in object && typeof object.stream === 'function') ||\n      ('arrayBuffer' in object && typeof object.arrayBuffer === 'function')\n    )\n  }\n}\n\n/**\n * @param {string} url The path to check for query strings or fragments.\n * @returns {boolean} Returns true if the path contains a query string or fragment.\n */\nfunction pathHasQueryOrFragment (url) {\n  return (\n    url.includes('?') ||\n    url.includes('#')\n  )\n}\n\n/**\n * @param {string} url The URL to add the query params to\n * @param {import('node:querystring').ParsedUrlQueryInput} queryParams The object to serialize into a URL query string\n * @returns {string} The URL with the query params added\n */\nfunction serializePathWithQuery (url, queryParams) {\n  if (pathHasQueryOrFragment(url)) {\n    throw new Error('Query params cannot be passed when url already contains \"?\" or \"#\".')\n  }\n\n  const stringified = stringify(queryParams)\n\n  if (stringified) {\n    url += '?' + stringified\n  }\n\n  return url\n}\n\n/**\n * @param {number|string|undefined} port\n * @returns {boolean}\n */\nfunction isValidPort (port) {\n  const value = parseInt(port, 10)\n  return (\n    value === Number(port) &&\n    value >= 0 &&\n    value <= 65535\n  )\n}\n\n/**\n * Check if the value is a valid http or https prefixed string.\n *\n * @param {string} value\n * @returns {boolean}\n */\nfunction isHttpOrHttpsPrefixed (value) {\n  return (\n    value != null &&\n    value[0] === 'h' &&\n    value[1] === 't' &&\n    value[2] === 't' &&\n    value[3] === 'p' &&\n    (\n      value[4] === ':' ||\n      (\n        value[4] === 's' &&\n        value[5] === ':'\n      )\n    )\n  )\n}\n\n/**\n * @param {string|URL|Record<string,string>} url\n * @returns {URL}\n */\nfunction parseURL (url) {\n  if (typeof url === 'string') {\n    /**\n     * @type {URL}\n     */\n    url = new URL(url)\n\n    if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {\n      throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')\n    }\n\n    return url\n  }\n\n  if (!url || typeof url !== 'object') {\n    throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.')\n  }\n\n  if (!(url instanceof URL)) {\n    if (url.port != null && url.port !== '' && isValidPort(url.port) === false) {\n      throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.')\n    }\n\n    if (url.path != null && typeof url.path !== 'string') {\n      throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.')\n    }\n\n    if (url.pathname != null && typeof url.pathname !== 'string') {\n      throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.')\n    }\n\n    if (url.hostname != null && typeof url.hostname !== 'string') {\n      throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.')\n    }\n\n    if (url.origin != null && typeof url.origin !== 'string') {\n      throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.')\n    }\n\n    if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {\n      throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')\n    }\n\n    const port = url.port != null\n      ? url.port\n      : (url.protocol === 'https:' ? 443 : 80)\n    let origin = url.origin != null\n      ? url.origin\n      : `${url.protocol || ''}//${url.hostname || ''}:${port}`\n    let path = url.path != null\n      ? url.path\n      : `${url.pathname || ''}${url.search || ''}`\n\n    if (origin[origin.length - 1] === '/') {\n      origin = origin.slice(0, origin.length - 1)\n    }\n\n    if (path && path[0] !== '/') {\n      path = `/${path}`\n    }\n    // new URL(path, origin) is unsafe when `path` contains an absolute URL\n    // From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL:\n    // If first parameter is a relative URL, second param is required, and will be used as the base URL.\n    // If first parameter is an absolute URL, a given second param will be ignored.\n    return new URL(`${origin}${path}`)\n  }\n\n  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {\n    throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.')\n  }\n\n  return url\n}\n\n/**\n * @param {string|URL|Record<string, string>} url\n * @returns {URL}\n */\nfunction parseOrigin (url) {\n  url = parseURL(url)\n\n  if (url.pathname !== '/' || url.search || url.hash) {\n    throw new InvalidArgumentError('invalid url')\n  }\n\n  return url\n}\n\n/**\n * @param {string} host\n * @returns {string}\n */\nfunction getHostname (host) {\n  if (host[0] === '[') {\n    const idx = host.indexOf(']')\n\n    assert(idx !== -1)\n    return host.substring(1, idx)\n  }\n\n  const idx = host.indexOf(':')\n  if (idx === -1) return host\n\n  return host.substring(0, idx)\n}\n\n/**\n * IP addresses are not valid server names per RFC6066\n * Currently, the only server names supported are DNS hostnames\n * @param {string|null} host\n * @returns {string|null}\n */\nfunction getServerName (host) {\n  if (!host) {\n    return null\n  }\n\n  assert(typeof host === 'string')\n\n  const servername = getHostname(host)\n  if (net.isIP(servername)) {\n    return ''\n  }\n\n  return servername\n}\n\n/**\n * @function\n * @template T\n * @param {T} obj\n * @returns {T}\n */\nfunction deepClone (obj) {\n  return JSON.parse(JSON.stringify(obj))\n}\n\n/**\n * @param {*} obj\n * @returns {obj is AsyncIterable}\n */\nfunction isAsyncIterable (obj) {\n  return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')\n}\n\n/**\n * @param {*} obj\n * @returns {obj is Iterable}\n */\nfunction isIterable (obj) {\n  return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))\n}\n\n/**\n * Checks whether an object has a safe Symbol.iterator — i.e. one that is\n * either own or inherited from a non-Object.prototype chain.  This prevents\n * prototype-pollution attacks from injecting a fake iterator on\n * Object.prototype.\n * @param {object} obj\n * @returns {boolean}\n */\nfunction hasSafeIterator (obj) {\n  const prototype = Object.getPrototypeOf(obj)\n  const ownIterator = Object.prototype.hasOwnProperty.call(obj, Symbol.iterator)\n  return ownIterator || (prototype != null && prototype !== Object.prototype && typeof obj[Symbol.iterator] === 'function')\n}\n\n/**\n * @param {Blob|Buffer|import ('stream').Stream} body\n * @returns {number|null}\n */\nfunction bodyLength (body) {\n  if (body == null) {\n    return 0\n  } else if (isStream(body)) {\n    const state = body._readableState\n    return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length)\n      ? state.length\n      : null\n  } else if (isBlobLike(body)) {\n    return body.size != null ? body.size : null\n  } else if (isBuffer(body)) {\n    return body.byteLength\n  }\n\n  return null\n}\n\n/**\n * @param {import ('stream').Stream} body\n * @returns {boolean}\n */\nfunction isDestroyed (body) {\n  return body && !!(body.destroyed || body[kDestroyed] || (stream.isDestroyed?.(body)))\n}\n\n/**\n * @param {import ('stream').Stream} stream\n * @param {Error} [err]\n * @returns {void}\n */\nfunction destroy (stream, err) {\n  if (stream == null || !isStream(stream) || isDestroyed(stream)) {\n    return\n  }\n\n  if (typeof stream.destroy === 'function') {\n    if (Object.getPrototypeOf(stream).constructor === IncomingMessage) {\n      // See: https://github.com/nodejs/node/pull/38505/files\n      stream.socket = null\n    }\n\n    stream.destroy(err)\n  } else if (err) {\n    queueMicrotask(() => {\n      stream.emit('error', err)\n    })\n  }\n\n  if (stream.destroyed !== true) {\n    stream[kDestroyed] = true\n  }\n}\n\nconst KEEPALIVE_TIMEOUT_EXPR = /timeout=(\\d+)/\n/**\n * @param {string} val\n * @returns {number | null}\n */\nfunction parseKeepAliveTimeout (val) {\n  const m = val.match(KEEPALIVE_TIMEOUT_EXPR)\n  return m ? parseInt(m[1], 10) * 1000 : null\n}\n\n/**\n * Retrieves a header name and returns its lowercase value.\n * @param {string | Buffer} value Header name\n * @returns {string}\n */\nfunction headerNameToString (value) {\n  return typeof value === 'string'\n    ? headerNameLowerCasedRecord[value] ?? value.toLowerCase()\n    : tree.lookup(value) ?? value.toString('latin1').toLowerCase()\n}\n\n/**\n * Receive the buffer as a string and return its lowercase value.\n * @param {Buffer} value Header name\n * @returns {string}\n */\nfunction bufferToLowerCasedHeaderName (value) {\n  return tree.lookup(value) ?? value.toString('latin1').toLowerCase()\n}\n\n/**\n * @param {(Buffer | string)[]} headers\n * @param {Record<string, string | string[]>} [obj]\n * @returns {Record<string, string | string[]>}\n */\nfunction parseHeaders (headers, obj) {\n  if (obj === undefined) obj = {}\n\n  for (let i = 0; i < headers.length; i += 2) {\n    const key = headerNameToString(headers[i])\n    let val = obj[key]\n\n    if (val) {\n      if (typeof val === 'string') {\n        val = [val]\n        obj[key] = val\n      }\n      val.push(headers[i + 1].toString('latin1'))\n    } else {\n      const headersValue = headers[i + 1]\n      if (typeof headersValue === 'string') {\n        obj[key] = headersValue\n      } else {\n        obj[key] = Array.isArray(headersValue) ? headersValue.map(x => x.toString('latin1')) : headersValue.toString('latin1')\n      }\n    }\n  }\n\n  return obj\n}\n\n/**\n * @param {Buffer[]} headers\n * @returns {string[]}\n */\nfunction parseRawHeaders (headers) {\n  const headersLength = headers.length\n  /**\n   * @type {string[]}\n   */\n  const ret = new Array(headersLength)\n\n  let key\n  let val\n\n  for (let n = 0; n < headersLength; n += 2) {\n    key = headers[n]\n    val = headers[n + 1]\n\n    typeof key !== 'string' && (key = key.toString())\n    typeof val !== 'string' && (val = val.toString('latin1'))\n\n    ret[n] = key\n    ret[n + 1] = val\n  }\n\n  return ret\n}\n\n/**\n * @param {string[]} headers\n * @param {Buffer[]} headers\n */\nfunction encodeRawHeaders (headers) {\n  if (!Array.isArray(headers)) {\n    throw new TypeError('expected headers to be an array')\n  }\n  return headers.map(x => Buffer.from(x))\n}\n\n/**\n * @param {*} buffer\n * @returns {buffer is Buffer}\n */\nfunction isBuffer (buffer) {\n  // See, https://github.com/mcollina/undici/pull/319\n  return buffer instanceof Uint8Array || Buffer.isBuffer(buffer)\n}\n\n/**\n * Asserts that the handler object is a request handler.\n *\n * @param {object} handler\n * @param {string} method\n * @param {string} [upgrade]\n * @returns {asserts handler is import('../api/api-request').RequestHandler}\n */\nfunction assertRequestHandler (handler, method, upgrade) {\n  if (!handler || typeof handler !== 'object') {\n    throw new InvalidArgumentError('handler must be an object')\n  }\n\n  if (typeof handler.onRequestStart === 'function') {\n    // TODO (fix): More checks...\n    return\n  }\n\n  if (typeof handler.onConnect !== 'function') {\n    throw new InvalidArgumentError('invalid onConnect method')\n  }\n\n  if (typeof handler.onError !== 'function') {\n    throw new InvalidArgumentError('invalid onError method')\n  }\n\n  if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) {\n    throw new InvalidArgumentError('invalid onBodySent method')\n  }\n\n  if (upgrade || method === 'CONNECT') {\n    if (typeof handler.onUpgrade !== 'function') {\n      throw new InvalidArgumentError('invalid onUpgrade method')\n    }\n  } else {\n    if (typeof handler.onHeaders !== 'function') {\n      throw new InvalidArgumentError('invalid onHeaders method')\n    }\n\n    if (typeof handler.onData !== 'function') {\n      throw new InvalidArgumentError('invalid onData method')\n    }\n\n    if (typeof handler.onComplete !== 'function') {\n      throw new InvalidArgumentError('invalid onComplete method')\n    }\n  }\n}\n\n/**\n * A body is disturbed if it has been read from and it cannot be re-used without\n * losing state or data.\n * @param {import('node:stream').Readable} body\n * @returns {boolean}\n */\nfunction isDisturbed (body) {\n  // TODO (fix): Why is body[kBodyUsed] needed?\n  return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))\n}\n\n/**\n * @typedef {object} SocketInfo\n * @property {string} [localAddress]\n * @property {number} [localPort]\n * @property {string} [remoteAddress]\n * @property {number} [remotePort]\n * @property {string} [remoteFamily]\n * @property {number} [timeout]\n * @property {number} bytesWritten\n * @property {number} bytesRead\n */\n\n/**\n * @param {import('net').Socket} socket\n * @returns {SocketInfo}\n */\nfunction getSocketInfo (socket) {\n  return {\n    localAddress: socket.localAddress,\n    localPort: socket.localPort,\n    remoteAddress: socket.remoteAddress,\n    remotePort: socket.remotePort,\n    remoteFamily: socket.remoteFamily,\n    timeout: socket.timeout,\n    bytesWritten: socket.bytesWritten,\n    bytesRead: socket.bytesRead\n  }\n}\n\n/**\n * @param {Iterable} iterable\n * @returns {ReadableStream}\n */\nfunction ReadableStreamFrom (iterable) {\n  // We cannot use ReadableStream.from here because it does not return a byte stream.\n\n  let iterator\n  return new ReadableStream(\n    {\n      start () {\n        iterator = iterable[Symbol.asyncIterator]()\n      },\n      pull (controller) {\n        return iterator.next().then(({ done, value }) => {\n          if (done) {\n            return queueMicrotask(() => {\n              controller.close()\n              controller.byobRequest?.respond(0)\n            })\n          } else {\n            const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)\n            if (buf.byteLength) {\n              return controller.enqueue(new Uint8Array(buf))\n            } else {\n              return this.pull(controller)\n            }\n          }\n        })\n      },\n      cancel () {\n        return iterator.return()\n      },\n      type: 'bytes'\n    }\n  )\n}\n\n/**\n * The object should be a FormData instance and contains all the required\n * methods.\n * @param {*} object\n * @returns {object is FormData}\n */\nfunction isFormDataLike (object) {\n  return (\n    object &&\n    typeof object === 'object' &&\n    typeof object.append === 'function' &&\n    typeof object.delete === 'function' &&\n    typeof object.get === 'function' &&\n    typeof object.getAll === 'function' &&\n    typeof object.has === 'function' &&\n    typeof object.set === 'function' &&\n    object[Symbol.toStringTag] === 'FormData'\n  )\n}\n\nfunction addAbortListener (signal, listener) {\n  if ('addEventListener' in signal) {\n    signal.addEventListener('abort', listener, { once: true })\n    return () => signal.removeEventListener('abort', listener)\n  }\n  signal.once('abort', listener)\n  return () => signal.removeListener('abort', listener)\n}\n\nconst validTokenChars = new Uint8Array([\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31\n  0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47 (!\"#$%&'()*+,-./)\n  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 (0-9:;<=>?)\n  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79 (@A-O)\n  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80-95 (P-Z[\\]^_)\n  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111 (`a-o)\n  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112-127 (p-z{|}~)\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-159\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-175\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-191\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-207\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-223\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-239\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  // 240-255\n])\n\n/**\n * @see https://tools.ietf.org/html/rfc7230#section-3.2.6\n * @param {number} c\n * @returns {boolean}\n */\nfunction isTokenCharCode (c) {\n  return (validTokenChars[c] === 1)\n}\n\nconst tokenRegExp = /^[\\^_`a-zA-Z\\-0-9!#$%&'*+.|~]+$/\n\n/**\n * @param {string} characters\n * @returns {boolean}\n */\nfunction isValidHTTPToken (characters) {\n  if (characters.length >= 12) return tokenRegExp.test(characters)\n  if (characters.length === 0) return false\n\n  for (let i = 0; i < characters.length; i++) {\n    if (validTokenChars[characters.charCodeAt(i)] !== 1) {\n      return false\n    }\n  }\n  return true\n}\n\n// headerCharRegex have been lifted from\n// https://github.com/nodejs/node/blob/main/lib/_http_common.js\n\n/**\n * Matches if val contains an invalid field-vchar\n *  field-value    = *( field-content / obs-fold )\n *  field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]\n *  field-vchar    = VCHAR / obs-text\n */\nconst headerCharRegex = /[^\\t\\x20-\\x7e\\x80-\\xff]/\n\n/**\n * @param {string} characters\n * @returns {boolean}\n */\nfunction isValidHeaderValue (characters) {\n  return !headerCharRegex.test(characters)\n}\n\nconst rangeHeaderRegex = /^bytes (\\d+)-(\\d+)\\/(\\d+)?$/\n\n/**\n * @typedef {object} RangeHeader\n * @property {number} start\n * @property {number | null} end\n * @property {number | null} size\n */\n\n/**\n * Parse accordingly to RFC 9110\n * @see https://www.rfc-editor.org/rfc/rfc9110#field.content-range\n * @param {string} [range]\n * @returns {RangeHeader|null}\n */\nfunction parseRangeHeader (range) {\n  if (range == null || range === '') return { start: 0, end: null, size: null }\n\n  const m = range ? range.match(rangeHeaderRegex) : null\n  return m\n    ? {\n        start: parseInt(m[1]),\n        end: m[2] ? parseInt(m[2]) : null,\n        size: m[3] ? parseInt(m[3]) : null\n      }\n    : null\n}\n\n/**\n * @template {import(\"events\").EventEmitter} T\n * @param {T} obj\n * @param {string} name\n * @param {(...args: any[]) => void} listener\n * @returns {T}\n */\nfunction addListener (obj, name, listener) {\n  const listeners = (obj[kListeners] ??= [])\n  listeners.push([name, listener])\n  obj.on(name, listener)\n  return obj\n}\n\n/**\n * @template {import(\"events\").EventEmitter} T\n * @param {T} obj\n * @returns {T}\n */\nfunction removeAllListeners (obj) {\n  if (obj[kListeners] != null) {\n    for (const [name, listener] of obj[kListeners]) {\n      obj.removeListener(name, listener)\n    }\n    obj[kListeners] = null\n  }\n  return obj\n}\n\n/**\n * @param {import ('../dispatcher/client')} client\n * @param {import ('../core/request')} request\n * @param {Error} err\n */\nfunction errorRequest (client, request, err) {\n  try {\n    request.onError(err)\n    assert(request.aborted)\n  } catch (err) {\n    client.emit('error', err)\n  }\n}\n\n/**\n * @param {WeakRef<net.Socket>} socketWeakRef\n * @param {object} opts\n * @param {number} opts.timeout\n * @param {string} opts.hostname\n * @param {number} opts.port\n * @returns {() => void}\n */\nconst setupConnectTimeout = process.platform === 'win32'\n  ? (socketWeakRef, opts) => {\n      if (!opts.timeout) {\n        return noop\n      }\n\n      let s1 = null\n      let s2 = null\n      const fastTimer = timers.setFastTimeout(() => {\n      // setImmediate is added to make sure that we prioritize socket error events over timeouts\n        s1 = setImmediate(() => {\n        // Windows needs an extra setImmediate probably due to implementation differences in the socket logic\n          s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts))\n        })\n      }, opts.timeout)\n      return () => {\n        timers.clearFastTimeout(fastTimer)\n        clearImmediate(s1)\n        clearImmediate(s2)\n      }\n    }\n  : (socketWeakRef, opts) => {\n      if (!opts.timeout) {\n        return noop\n      }\n\n      let s1 = null\n      const fastTimer = timers.setFastTimeout(() => {\n      // setImmediate is added to make sure that we prioritize socket error events over timeouts\n        s1 = setImmediate(() => {\n          onConnectTimeout(socketWeakRef.deref(), opts)\n        })\n      }, opts.timeout)\n      return () => {\n        timers.clearFastTimeout(fastTimer)\n        clearImmediate(s1)\n      }\n    }\n\n/**\n * @param {net.Socket} socket\n * @param {object} opts\n * @param {number} opts.timeout\n * @param {string} opts.hostname\n * @param {number} opts.port\n */\nfunction onConnectTimeout (socket, opts) {\n  // The socket could be already garbage collected\n  if (socket == null) {\n    return\n  }\n\n  let message = 'Connect Timeout Error'\n  if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {\n    message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`\n  } else {\n    message += ` (attempted address: ${opts.hostname}:${opts.port},`\n  }\n\n  message += ` timeout: ${opts.timeout}ms)`\n\n  destroy(socket, new ConnectTimeoutError(message))\n}\n\n/**\n * @param {string} urlString\n * @returns {string}\n */\nfunction getProtocolFromUrlString (urlString) {\n  if (\n    urlString[0] === 'h' &&\n    urlString[1] === 't' &&\n    urlString[2] === 't' &&\n    urlString[3] === 'p'\n  ) {\n    switch (urlString[4]) {\n      case ':':\n        return 'http:'\n      case 's':\n        if (urlString[5] === ':') {\n          return 'https:'\n        }\n    }\n  }\n  // fallback if none of the usual suspects\n  return urlString.slice(0, urlString.indexOf(':') + 1)\n}\n\nconst kEnumerableProperty = Object.create(null)\nkEnumerableProperty.enumerable = true\n\nconst normalizedMethodRecordsBase = {\n  delete: 'DELETE',\n  DELETE: 'DELETE',\n  get: 'GET',\n  GET: 'GET',\n  head: 'HEAD',\n  HEAD: 'HEAD',\n  options: 'OPTIONS',\n  OPTIONS: 'OPTIONS',\n  post: 'POST',\n  POST: 'POST',\n  put: 'PUT',\n  PUT: 'PUT'\n}\n\nconst normalizedMethodRecords = {\n  ...normalizedMethodRecordsBase,\n  patch: 'patch',\n  PATCH: 'PATCH'\n}\n\n// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.\nObject.setPrototypeOf(normalizedMethodRecordsBase, null)\nObject.setPrototypeOf(normalizedMethodRecords, null)\n\nmodule.exports = {\n  kEnumerableProperty,\n  isDisturbed,\n  isBlobLike,\n  parseOrigin,\n  parseURL,\n  getServerName,\n  isStream,\n  isIterable,\n  hasSafeIterator,\n  isAsyncIterable,\n  isDestroyed,\n  headerNameToString,\n  bufferToLowerCasedHeaderName,\n  addListener,\n  removeAllListeners,\n  errorRequest,\n  parseRawHeaders,\n  encodeRawHeaders,\n  parseHeaders,\n  parseKeepAliveTimeout,\n  destroy,\n  bodyLength,\n  deepClone,\n  ReadableStreamFrom,\n  isBuffer,\n  assertRequestHandler,\n  getSocketInfo,\n  isFormDataLike,\n  pathHasQueryOrFragment,\n  serializePathWithQuery,\n  addAbortListener,\n  isValidHTTPToken,\n  isValidHeaderValue,\n  isTokenCharCode,\n  parseRangeHeader,\n  normalizedMethodRecordsBase,\n  normalizedMethodRecords,\n  isValidPort,\n  isHttpOrHttpsPrefixed,\n  nodeMajor,\n  nodeMinor,\n  safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),\n  wrapRequestBody,\n  setupConnectTimeout,\n  getProtocolFromUrlString\n}\n"
  },
  {
    "path": "lib/dispatcher/agent.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError, MaxOriginsReachedError } = require('../core/errors')\nconst { kClients, kRunning, kClose, kDestroy, kDispatch, kUrl } = require('../core/symbols')\nconst DispatcherBase = require('./dispatcher-base')\nconst Pool = require('./pool')\nconst Client = require('./client')\nconst util = require('../core/util')\n\nconst kOnConnect = Symbol('onConnect')\nconst kOnDisconnect = Symbol('onDisconnect')\nconst kOnConnectionError = Symbol('onConnectionError')\nconst kOnDrain = Symbol('onDrain')\nconst kFactory = Symbol('factory')\nconst kOptions = Symbol('options')\nconst kOrigins = Symbol('origins')\n\nfunction defaultFactory (origin, opts) {\n  return opts && opts.connections === 1\n    ? new Client(origin, opts)\n    : new Pool(origin, opts)\n}\n\nclass Agent extends DispatcherBase {\n  constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, ...options } = {}) {\n    if (typeof factory !== 'function') {\n      throw new InvalidArgumentError('factory must be a function.')\n    }\n\n    if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {\n      throw new InvalidArgumentError('connect must be a function or an object')\n    }\n\n    if (typeof maxOrigins !== 'number' || Number.isNaN(maxOrigins) || maxOrigins <= 0) {\n      throw new InvalidArgumentError('maxOrigins must be a number greater than 0')\n    }\n\n    super()\n\n    if (connect && typeof connect !== 'function') {\n      connect = { ...connect }\n    }\n\n    this[kOptions] = { ...util.deepClone(options), maxOrigins, connect }\n    this[kFactory] = factory\n    this[kClients] = new Map()\n    this[kOrigins] = new Set()\n\n    this[kOnDrain] = (origin, targets) => {\n      this.emit('drain', origin, [this, ...targets])\n    }\n\n    this[kOnConnect] = (origin, targets) => {\n      this.emit('connect', origin, [this, ...targets])\n    }\n\n    this[kOnDisconnect] = (origin, targets, err) => {\n      this.emit('disconnect', origin, [this, ...targets], err)\n    }\n\n    this[kOnConnectionError] = (origin, targets, err) => {\n      this.emit('connectionError', origin, [this, ...targets], err)\n    }\n  }\n\n  get [kRunning] () {\n    let ret = 0\n    for (const { dispatcher } of this[kClients].values()) {\n      ret += dispatcher[kRunning]\n    }\n    return ret\n  }\n\n  [kDispatch] (opts, handler) {\n    let key\n    if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {\n      key = String(opts.origin)\n    } else {\n      throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')\n    }\n\n    if (this[kOrigins].size >= this[kOptions].maxOrigins && !this[kOrigins].has(key)) {\n      throw new MaxOriginsReachedError()\n    }\n\n    const result = this[kClients].get(key)\n    let dispatcher = result && result.dispatcher\n    if (!dispatcher) {\n      const closeClientIfUnused = (connected) => {\n        const result = this[kClients].get(key)\n        if (result) {\n          if (connected) result.count -= 1\n          if (result.count <= 0) {\n            this[kClients].delete(key)\n            if (!result.dispatcher.destroyed) {\n              result.dispatcher.close()\n            }\n          }\n          this[kOrigins].delete(key)\n        }\n      }\n      dispatcher = this[kFactory](opts.origin, this[kOptions])\n        .on('drain', this[kOnDrain])\n        .on('connect', (origin, targets) => {\n          const result = this[kClients].get(key)\n          if (result) {\n            result.count += 1\n          }\n          this[kOnConnect](origin, targets)\n        })\n        .on('disconnect', (origin, targets, err) => {\n          closeClientIfUnused(true)\n          this[kOnDisconnect](origin, targets, err)\n        })\n        .on('connectionError', (origin, targets, err) => {\n          closeClientIfUnused(false)\n          this[kOnConnectionError](origin, targets, err)\n        })\n\n      this[kClients].set(key, { count: 0, dispatcher })\n      this[kOrigins].add(key)\n    }\n\n    return dispatcher.dispatch(opts, handler)\n  }\n\n  [kClose] () {\n    const closePromises = []\n    for (const { dispatcher } of this[kClients].values()) {\n      closePromises.push(dispatcher.close())\n    }\n    this[kClients].clear()\n\n    return Promise.all(closePromises)\n  }\n\n  [kDestroy] (err) {\n    const destroyPromises = []\n    for (const { dispatcher } of this[kClients].values()) {\n      destroyPromises.push(dispatcher.destroy(err))\n    }\n    this[kClients].clear()\n\n    return Promise.all(destroyPromises)\n  }\n\n  get stats () {\n    const allClientStats = {}\n    for (const { dispatcher } of this[kClients].values()) {\n      if (dispatcher.stats) {\n        allClientStats[dispatcher[kUrl].origin] = dispatcher.stats\n      }\n    }\n    return allClientStats\n  }\n}\n\nmodule.exports = Agent\n"
  },
  {
    "path": "lib/dispatcher/balanced-pool.js",
    "content": "'use strict'\n\nconst {\n  BalancedPoolMissingUpstreamError,\n  InvalidArgumentError\n} = require('../core/errors')\nconst {\n  PoolBase,\n  kClients,\n  kNeedDrain,\n  kAddClient,\n  kRemoveClient,\n  kGetDispatcher\n} = require('./pool-base')\nconst Pool = require('./pool')\nconst { kUrl } = require('../core/symbols')\nconst util = require('../core/util')\nconst kFactory = Symbol('factory')\n\nconst kOptions = Symbol('options')\nconst kGreatestCommonDivisor = Symbol('kGreatestCommonDivisor')\nconst kCurrentWeight = Symbol('kCurrentWeight')\nconst kIndex = Symbol('kIndex')\nconst kWeight = Symbol('kWeight')\nconst kMaxWeightPerServer = Symbol('kMaxWeightPerServer')\nconst kErrorPenalty = Symbol('kErrorPenalty')\n\n/**\n * Calculate the greatest common divisor of two numbers by\n * using the Euclidean algorithm.\n *\n * @param {number} a\n * @param {number} b\n * @returns {number}\n */\nfunction getGreatestCommonDivisor (a, b) {\n  if (a === 0) return b\n\n  while (b !== 0) {\n    const t = b\n    b = a % b\n    a = t\n  }\n  return a\n}\n\nfunction defaultFactory (origin, opts) {\n  return new Pool(origin, opts)\n}\n\nclass BalancedPool extends PoolBase {\n  constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) {\n    if (typeof factory !== 'function') {\n      throw new InvalidArgumentError('factory must be a function.')\n    }\n\n    super()\n\n    this[kOptions] = { ...util.deepClone(opts) }\n    this[kOptions].interceptors = opts.interceptors\n      ? { ...opts.interceptors }\n      : undefined\n    this[kIndex] = -1\n    this[kCurrentWeight] = 0\n\n    this[kMaxWeightPerServer] = this[kOptions].maxWeightPerServer || 100\n    this[kErrorPenalty] = this[kOptions].errorPenalty || 15\n\n    if (!Array.isArray(upstreams)) {\n      upstreams = [upstreams]\n    }\n\n    this[kFactory] = factory\n\n    for (const upstream of upstreams) {\n      this.addUpstream(upstream)\n    }\n    this._updateBalancedPoolStats()\n  }\n\n  addUpstream (upstream) {\n    const upstreamOrigin = util.parseOrigin(upstream).origin\n\n    if (this[kClients].find((pool) => (\n      pool[kUrl].origin === upstreamOrigin &&\n      pool.closed !== true &&\n      pool.destroyed !== true\n    ))) {\n      return this\n    }\n    const pool = this[kFactory](upstreamOrigin, this[kOptions])\n\n    this[kAddClient](pool)\n    pool.on('connect', () => {\n      pool[kWeight] = Math.min(this[kMaxWeightPerServer], pool[kWeight] + this[kErrorPenalty])\n    })\n\n    pool.on('connectionError', () => {\n      pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])\n      this._updateBalancedPoolStats()\n    })\n\n    pool.on('disconnect', (...args) => {\n      const err = args[2]\n      if (err && err.code === 'UND_ERR_SOCKET') {\n        // decrease the weight of the pool.\n        pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])\n        this._updateBalancedPoolStats()\n      }\n    })\n\n    for (const client of this[kClients]) {\n      client[kWeight] = this[kMaxWeightPerServer]\n    }\n\n    this._updateBalancedPoolStats()\n\n    return this\n  }\n\n  _updateBalancedPoolStats () {\n    let result = 0\n    for (let i = 0; i < this[kClients].length; i++) {\n      result = getGreatestCommonDivisor(this[kClients][i][kWeight], result)\n    }\n\n    this[kGreatestCommonDivisor] = result\n  }\n\n  removeUpstream (upstream) {\n    const upstreamOrigin = util.parseOrigin(upstream).origin\n\n    const pool = this[kClients].find((pool) => (\n      pool[kUrl].origin === upstreamOrigin &&\n      pool.closed !== true &&\n      pool.destroyed !== true\n    ))\n\n    if (pool) {\n      this[kRemoveClient](pool)\n    }\n\n    return this\n  }\n\n  getUpstream (upstream) {\n    const upstreamOrigin = util.parseOrigin(upstream).origin\n\n    return this[kClients].find((pool) => (\n      pool[kUrl].origin === upstreamOrigin &&\n      pool.closed !== true &&\n      pool.destroyed !== true\n    ))\n  }\n\n  get upstreams () {\n    return this[kClients]\n      .filter(dispatcher => dispatcher.closed !== true && dispatcher.destroyed !== true)\n      .map((p) => p[kUrl].origin)\n  }\n\n  [kGetDispatcher] () {\n    // We validate that pools is greater than 0,\n    // otherwise we would have to wait until an upstream\n    // is added, which might never happen.\n    if (this[kClients].length === 0) {\n      throw new BalancedPoolMissingUpstreamError()\n    }\n\n    const dispatcher = this[kClients].find(dispatcher => (\n      !dispatcher[kNeedDrain] &&\n      dispatcher.closed !== true &&\n      dispatcher.destroyed !== true\n    ))\n\n    if (!dispatcher) {\n      return\n    }\n\n    const allClientsBusy = this[kClients].map(pool => pool[kNeedDrain]).reduce((a, b) => a && b, true)\n\n    if (allClientsBusy) {\n      return\n    }\n\n    let counter = 0\n\n    let maxWeightIndex = this[kClients].findIndex(pool => !pool[kNeedDrain])\n\n    while (counter++ < this[kClients].length) {\n      this[kIndex] = (this[kIndex] + 1) % this[kClients].length\n      const pool = this[kClients][this[kIndex]]\n\n      // find pool index with the largest weight\n      if (pool[kWeight] > this[kClients][maxWeightIndex][kWeight] && !pool[kNeedDrain]) {\n        maxWeightIndex = this[kIndex]\n      }\n\n      // decrease the current weight every `this[kClients].length`.\n      if (this[kIndex] === 0) {\n        // Set the current weight to the next lower weight.\n        this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor]\n\n        if (this[kCurrentWeight] <= 0) {\n          this[kCurrentWeight] = this[kMaxWeightPerServer]\n        }\n      }\n      if (pool[kWeight] >= this[kCurrentWeight] && (!pool[kNeedDrain])) {\n        return pool\n      }\n    }\n\n    this[kCurrentWeight] = this[kClients][maxWeightIndex][kWeight]\n    this[kIndex] = maxWeightIndex\n    return this[kClients][maxWeightIndex]\n  }\n}\n\nmodule.exports = BalancedPool\n"
  },
  {
    "path": "lib/dispatcher/client-h1.js",
    "content": "'use strict'\n\n/* global WebAssembly */\n\nconst assert = require('node:assert')\nconst util = require('../core/util.js')\nconst { channels } = require('../core/diagnostics.js')\nconst timers = require('../util/timers.js')\nconst {\n  RequestContentLengthMismatchError,\n  ResponseContentLengthMismatchError,\n  RequestAbortedError,\n  HeadersTimeoutError,\n  HeadersOverflowError,\n  SocketError,\n  InformationalError,\n  BodyTimeoutError,\n  HTTPParserError,\n  ResponseExceededMaxSizeError\n} = require('../core/errors.js')\nconst {\n  kUrl,\n  kReset,\n  kClient,\n  kParser,\n  kBlocking,\n  kRunning,\n  kPending,\n  kSize,\n  kWriting,\n  kQueue,\n  kNoRef,\n  kKeepAliveDefaultTimeout,\n  kHostHeader,\n  kPendingIdx,\n  kRunningIdx,\n  kError,\n  kPipelining,\n  kSocket,\n  kKeepAliveTimeoutValue,\n  kMaxHeadersSize,\n  kKeepAliveMaxTimeout,\n  kKeepAliveTimeoutThreshold,\n  kHeadersTimeout,\n  kBodyTimeout,\n  kStrictContentLength,\n  kMaxRequests,\n  kCounter,\n  kMaxResponseSize,\n  kOnError,\n  kResume,\n  kHTTPContext,\n  kClosed\n} = require('../core/symbols.js')\n\nconst constants = require('../llhttp/constants.js')\nconst EMPTY_BUF = Buffer.alloc(0)\nconst FastBuffer = Buffer[Symbol.species]\nconst removeAllListeners = util.removeAllListeners\n\nlet extractBody\n\nfunction lazyllhttp () {\n  const llhttpWasmData = process.env.JEST_WORKER_ID ? require('../llhttp/llhttp-wasm.js') : undefined\n\n  let mod\n\n  // We disable wasm SIMD on ppc64 as it seems to be broken on Power 9 architectures.\n  let useWasmSIMD = process.arch !== 'ppc64'\n  // The Env Variable UNDICI_NO_WASM_SIMD allows explicitly overriding the default behavior\n  if (process.env.UNDICI_NO_WASM_SIMD === '1') {\n    useWasmSIMD = true\n  } else if (process.env.UNDICI_NO_WASM_SIMD === '0') {\n    useWasmSIMD = false\n  }\n\n  if (useWasmSIMD) {\n    try {\n      mod = new WebAssembly.Module(require('../llhttp/llhttp_simd-wasm.js'))\n    } catch {\n    }\n  }\n\n  if (!mod) {\n    // We could check if the error was caused by the simd option not\n    // being enabled, but the occurring of this other error\n    // * https://github.com/emscripten-core/emscripten/issues/11495\n    // got me to remove that check to avoid breaking Node 12.\n    mod = new WebAssembly.Module(llhttpWasmData || require('../llhttp/llhttp-wasm.js'))\n  }\n\n  return new WebAssembly.Instance(mod, {\n    env: {\n      /**\n       * @param {number} p\n       * @param {number} at\n       * @param {number} len\n       * @returns {number}\n       */\n      wasm_on_url: (p, at, len) => {\n        return 0\n      },\n      /**\n       * @param {number} p\n       * @param {number} at\n       * @param {number} len\n       * @returns {number}\n       */\n      wasm_on_status: (p, at, len) => {\n        assert(currentParser.ptr === p)\n        const start = at - currentBufferPtr + currentBufferRef.byteOffset\n        return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len))\n      },\n      /**\n       * @param {number} p\n       * @returns {number}\n       */\n      wasm_on_message_begin: (p) => {\n        assert(currentParser.ptr === p)\n        return currentParser.onMessageBegin()\n      },\n      /**\n       * @param {number} p\n       * @param {number} at\n       * @param {number} len\n       * @returns {number}\n       */\n      wasm_on_header_field: (p, at, len) => {\n        assert(currentParser.ptr === p)\n        const start = at - currentBufferPtr + currentBufferRef.byteOffset\n        return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len))\n      },\n      /**\n       * @param {number} p\n       * @param {number} at\n       * @param {number} len\n       * @returns {number}\n       */\n      wasm_on_header_value: (p, at, len) => {\n        assert(currentParser.ptr === p)\n        const start = at - currentBufferPtr + currentBufferRef.byteOffset\n        return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len))\n      },\n      /**\n       * @param {number} p\n       * @param {number} statusCode\n       * @param {0|1} upgrade\n       * @param {0|1} shouldKeepAlive\n       * @returns {number}\n       */\n      wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {\n        assert(currentParser.ptr === p)\n        return currentParser.onHeadersComplete(statusCode, upgrade === 1, shouldKeepAlive === 1)\n      },\n      /**\n       * @param {number} p\n       * @param {number} at\n       * @param {number} len\n       * @returns {number}\n       */\n      wasm_on_body: (p, at, len) => {\n        assert(currentParser.ptr === p)\n        const start = at - currentBufferPtr + currentBufferRef.byteOffset\n        return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len))\n      },\n      /**\n       * @param {number} p\n       * @returns {number}\n       */\n      wasm_on_message_complete: (p) => {\n        assert(currentParser.ptr === p)\n        return currentParser.onMessageComplete()\n      }\n\n    }\n  })\n}\n\nlet llhttpInstance = null\n\n/**\n * @type {Parser|null}\n */\nlet currentParser = null\nlet currentBufferRef = null\n/**\n * @type {number}\n */\nlet currentBufferSize = 0\nlet currentBufferPtr = null\n\nconst USE_NATIVE_TIMER = 0\nconst USE_FAST_TIMER = 1\n\n// Use fast timers for headers and body to take eventual event loop\n// latency into account.\nconst TIMEOUT_HEADERS = 2 | USE_FAST_TIMER\nconst TIMEOUT_BODY = 4 | USE_FAST_TIMER\n\n// Use native timers to ignore event loop latency for keep-alive\n// handling.\nconst TIMEOUT_KEEP_ALIVE = 8 | USE_NATIVE_TIMER\n\nclass Parser {\n  /**\n     * @param {import('./client.js')} client\n     * @param {import('net').Socket} socket\n     * @param {*} llhttp\n     */\n  constructor (client, socket, { exports }) {\n    this.llhttp = exports\n    this.ptr = this.llhttp.llhttp_alloc(constants.TYPE.RESPONSE)\n    this.client = client\n    /**\n     * @type {import('net').Socket}\n     */\n    this.socket = socket\n    this.timeout = null\n    this.timeoutValue = null\n    this.timeoutType = null\n    this.statusCode = 0\n    this.statusText = ''\n    this.upgrade = false\n    this.headers = []\n    this.headersSize = 0\n    this.headersMaxSize = client[kMaxHeadersSize]\n    this.shouldKeepAlive = false\n    this.paused = false\n    this.resume = this.resume.bind(this)\n\n    this.bytesRead = 0\n\n    this.keepAlive = ''\n    this.contentLength = ''\n    this.connection = ''\n    this.maxResponseSize = client[kMaxResponseSize]\n  }\n\n  setTimeout (delay, type) {\n    // If the existing timer and the new timer are of different timer type\n    // (fast or native) or have different delay, we need to clear the existing\n    // timer and set a new one.\n    if (\n      delay !== this.timeoutValue ||\n      (type & USE_FAST_TIMER) ^ (this.timeoutType & USE_FAST_TIMER)\n    ) {\n      // If a timeout is already set, clear it with clearTimeout of the fast\n      // timer implementation, as it can clear fast and native timers.\n      if (this.timeout) {\n        timers.clearTimeout(this.timeout)\n        this.timeout = null\n      }\n\n      if (delay) {\n        if (type & USE_FAST_TIMER) {\n          this.timeout = timers.setFastTimeout(onParserTimeout, delay, new WeakRef(this))\n        } else {\n          this.timeout = setTimeout(onParserTimeout, delay, new WeakRef(this))\n          this.timeout?.unref()\n        }\n      }\n\n      this.timeoutValue = delay\n    } else if (this.timeout) {\n      if (this.timeout.refresh) {\n        this.timeout.refresh()\n      }\n    }\n\n    this.timeoutType = type\n  }\n\n  resume () {\n    if (this.socket.destroyed || !this.paused) {\n      return\n    }\n\n    assert(this.ptr != null)\n    assert(currentParser === null)\n\n    this.llhttp.llhttp_resume(this.ptr)\n\n    assert(this.timeoutType === TIMEOUT_BODY)\n    if (this.timeout) {\n      if (this.timeout.refresh) {\n        this.timeout.refresh()\n      }\n    }\n\n    this.paused = false\n    this.execute(this.socket.read() || EMPTY_BUF) // Flush parser.\n    this.readMore()\n  }\n\n  readMore () {\n    while (!this.paused && this.ptr) {\n      const chunk = this.socket.read()\n      if (chunk === null) {\n        break\n      }\n      this.execute(chunk)\n    }\n  }\n\n  /**\n   * @param {Buffer} chunk\n   */\n  execute (chunk) {\n    assert(currentParser === null)\n    assert(this.ptr != null)\n    assert(!this.paused)\n\n    const { socket, llhttp } = this\n\n    // Allocate a new buffer if the current buffer is too small.\n    if (chunk.length > currentBufferSize) {\n      if (currentBufferPtr) {\n        llhttp.free(currentBufferPtr)\n      }\n      // Allocate a buffer that is a multiple of 4096 bytes.\n      currentBufferSize = Math.ceil(chunk.length / 4096) * 4096\n      currentBufferPtr = llhttp.malloc(currentBufferSize)\n    }\n\n    new Uint8Array(llhttp.memory.buffer, currentBufferPtr, currentBufferSize).set(chunk)\n\n    // Call `execute` on the wasm parser.\n    // We pass the `llhttp_parser` pointer address, the pointer address of buffer view data,\n    // and finally the length of bytes to parse.\n    // The return value is an error code or `constants.ERROR.OK`.\n    try {\n      let ret\n\n      try {\n        currentBufferRef = chunk\n        currentParser = this\n        ret = llhttp.llhttp_execute(this.ptr, currentBufferPtr, chunk.length)\n      } finally {\n        currentParser = null\n        currentBufferRef = null\n      }\n\n      if (ret !== constants.ERROR.OK) {\n        const data = chunk.subarray(llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr)\n\n        if (ret === constants.ERROR.PAUSED_UPGRADE) {\n          this.onUpgrade(data)\n        } else if (ret === constants.ERROR.PAUSED) {\n          this.paused = true\n          socket.unshift(data)\n        } else {\n          const ptr = llhttp.llhttp_get_error_reason(this.ptr)\n          let message = ''\n          if (ptr) {\n            const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)\n            message =\n              'Response does not match the HTTP/1.1 protocol (' +\n              Buffer.from(llhttp.memory.buffer, ptr, len).toString() +\n              ')'\n          }\n          throw new HTTPParserError(message, constants.ERROR[ret], data)\n        }\n      }\n    } catch (err) {\n      util.destroy(socket, err)\n    }\n  }\n\n  destroy () {\n    assert(currentParser === null)\n    assert(this.ptr != null)\n\n    this.llhttp.llhttp_free(this.ptr)\n    this.ptr = null\n\n    this.timeout && timers.clearTimeout(this.timeout)\n    this.timeout = null\n    this.timeoutValue = null\n    this.timeoutType = null\n\n    this.paused = false\n  }\n\n  /**\n   * @param {Buffer} buf\n   * @returns {0}\n   */\n  onStatus (buf) {\n    this.statusText = buf.toString()\n    return 0\n  }\n\n  /**\n   * @returns {0|-1}\n   */\n  onMessageBegin () {\n    const { socket, client } = this\n\n    if (socket.destroyed) {\n      return -1\n    }\n\n    const request = client[kQueue][client[kRunningIdx]]\n    if (!request) {\n      return -1\n    }\n    request.onResponseStarted()\n\n    return 0\n  }\n\n  /**\n   * @param {Buffer} buf\n   * @returns {number}\n   */\n  onHeaderField (buf) {\n    const len = this.headers.length\n\n    if ((len & 1) === 0) {\n      this.headers.push(buf)\n    } else {\n      this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf])\n    }\n\n    this.trackHeader(buf.length)\n\n    return 0\n  }\n\n  /**\n   * @param {Buffer} buf\n   * @returns {number}\n   */\n  onHeaderValue (buf) {\n    let len = this.headers.length\n\n    if ((len & 1) === 1) {\n      this.headers.push(buf)\n      len += 1\n    } else {\n      this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf])\n    }\n\n    const key = this.headers[len - 2]\n    if (key.length === 10) {\n      const headerName = util.bufferToLowerCasedHeaderName(key)\n      if (headerName === 'keep-alive') {\n        this.keepAlive += buf.toString()\n      } else if (headerName === 'connection') {\n        this.connection += buf.toString()\n      }\n    } else if (key.length === 14 && util.bufferToLowerCasedHeaderName(key) === 'content-length') {\n      this.contentLength += buf.toString()\n    }\n\n    this.trackHeader(buf.length)\n\n    return 0\n  }\n\n  /**\n   * @param {number} len\n   */\n  trackHeader (len) {\n    this.headersSize += len\n    if (this.headersSize >= this.headersMaxSize) {\n      util.destroy(this.socket, new HeadersOverflowError())\n    }\n  }\n\n  /**\n   * @param {Buffer} head\n   */\n  onUpgrade (head) {\n    const { upgrade, client, socket, headers, statusCode } = this\n\n    assert(upgrade)\n    assert(client[kSocket] === socket)\n    assert(!socket.destroyed)\n    assert(!this.paused)\n    assert((headers.length & 1) === 0)\n\n    const request = client[kQueue][client[kRunningIdx]]\n    assert(request)\n    assert(request.upgrade || request.method === 'CONNECT')\n\n    this.statusCode = 0\n    this.statusText = ''\n    this.shouldKeepAlive = false\n\n    this.headers = []\n    this.headersSize = 0\n\n    socket.unshift(head)\n\n    socket[kParser].destroy()\n    socket[kParser] = null\n\n    socket[kClient] = null\n    socket[kError] = null\n\n    removeAllListeners(socket)\n\n    client[kSocket] = null\n    client[kHTTPContext] = null // TODO (fix): This is hacky...\n    client[kQueue][client[kRunningIdx]++] = null\n    client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade'))\n\n    try {\n      request.onUpgrade(statusCode, headers, socket)\n    } catch (err) {\n      util.destroy(socket, err)\n    }\n\n    client[kResume]()\n  }\n\n  /**\n   * @param {number} statusCode\n   * @param {boolean} upgrade\n   * @param {boolean} shouldKeepAlive\n   * @returns {number}\n   */\n  onHeadersComplete (statusCode, upgrade, shouldKeepAlive) {\n    const { client, socket, headers, statusText } = this\n\n    if (socket.destroyed) {\n      return -1\n    }\n\n    const request = client[kQueue][client[kRunningIdx]]\n\n    if (!request) {\n      return -1\n    }\n\n    assert(!this.upgrade)\n    assert(this.statusCode < 200)\n\n    if (statusCode === 100) {\n      util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))\n      return -1\n    }\n\n    /* this can only happen if server is misbehaving */\n    if (upgrade && !request.upgrade) {\n      util.destroy(socket, new SocketError('bad upgrade', util.getSocketInfo(socket)))\n      return -1\n    }\n\n    assert(this.timeoutType === TIMEOUT_HEADERS)\n\n    this.statusCode = statusCode\n    this.shouldKeepAlive = (\n      shouldKeepAlive ||\n      // Override llhttp value which does not allow keepAlive for HEAD.\n      (request.method === 'HEAD' && !socket[kReset] && this.connection.toLowerCase() === 'keep-alive')\n    )\n\n    if (this.statusCode >= 200) {\n      const bodyTimeout = request.bodyTimeout != null\n        ? request.bodyTimeout\n        : client[kBodyTimeout]\n      this.setTimeout(bodyTimeout, TIMEOUT_BODY)\n    } else if (this.timeout) {\n      if (this.timeout.refresh) {\n        this.timeout.refresh()\n      }\n    }\n\n    if (request.method === 'CONNECT') {\n      assert(client[kRunning] === 1)\n      this.upgrade = true\n      return 2\n    }\n\n    if (upgrade) {\n      assert(client[kRunning] === 1)\n      this.upgrade = true\n      return 2\n    }\n\n    assert((this.headers.length & 1) === 0)\n    this.headers = []\n    this.headersSize = 0\n\n    if (this.shouldKeepAlive && client[kPipelining]) {\n      const keepAliveTimeout = this.keepAlive ? util.parseKeepAliveTimeout(this.keepAlive) : null\n\n      if (keepAliveTimeout != null) {\n        const timeout = Math.min(\n          keepAliveTimeout - client[kKeepAliveTimeoutThreshold],\n          client[kKeepAliveMaxTimeout]\n        )\n        if (timeout <= 0) {\n          socket[kReset] = true\n        } else {\n          client[kKeepAliveTimeoutValue] = timeout\n        }\n      } else {\n        client[kKeepAliveTimeoutValue] = client[kKeepAliveDefaultTimeout]\n      }\n    } else {\n      // Stop more requests from being dispatched.\n      socket[kReset] = true\n    }\n\n    const pause = request.onHeaders(statusCode, headers, this.resume, statusText) === false\n\n    if (request.aborted) {\n      return -1\n    }\n\n    if (request.method === 'HEAD') {\n      return 1\n    }\n\n    if (statusCode < 200) {\n      return 1\n    }\n\n    if (socket[kBlocking]) {\n      socket[kBlocking] = false\n      client[kResume]()\n    }\n\n    return pause ? constants.ERROR.PAUSED : 0\n  }\n\n  /**\n   * @param {Buffer} buf\n   * @returns {number}\n   */\n  onBody (buf) {\n    const { client, socket, statusCode, maxResponseSize } = this\n\n    if (socket.destroyed) {\n      return -1\n    }\n\n    const request = client[kQueue][client[kRunningIdx]]\n    assert(request)\n\n    assert(this.timeoutType === TIMEOUT_BODY)\n    if (this.timeout) {\n      if (this.timeout.refresh) {\n        this.timeout.refresh()\n      }\n    }\n\n    assert(statusCode >= 200)\n\n    if (maxResponseSize > -1 && this.bytesRead + buf.length > maxResponseSize) {\n      util.destroy(socket, new ResponseExceededMaxSizeError())\n      return -1\n    }\n\n    this.bytesRead += buf.length\n\n    if (request.onData(buf) === false) {\n      return constants.ERROR.PAUSED\n    }\n\n    return 0\n  }\n\n  /**\n   * @returns {number}\n   */\n  onMessageComplete () {\n    const { client, socket, statusCode, upgrade, headers, contentLength, bytesRead, shouldKeepAlive } = this\n\n    if (socket.destroyed && (!statusCode || shouldKeepAlive)) {\n      return -1\n    }\n\n    if (upgrade) {\n      return 0\n    }\n\n    assert(statusCode >= 100)\n    assert((this.headers.length & 1) === 0)\n\n    const request = client[kQueue][client[kRunningIdx]]\n    assert(request)\n\n    this.statusCode = 0\n    this.statusText = ''\n    this.bytesRead = 0\n    this.contentLength = ''\n    this.keepAlive = ''\n    this.connection = ''\n\n    this.headers = []\n    this.headersSize = 0\n\n    if (statusCode < 200) {\n      return 0\n    }\n\n    if (request.method !== 'HEAD' && contentLength && bytesRead !== parseInt(contentLength, 10)) {\n      util.destroy(socket, new ResponseContentLengthMismatchError())\n      return -1\n    }\n\n    request.onComplete(headers)\n\n    client[kQueue][client[kRunningIdx]++] = null\n\n    if (socket[kWriting]) {\n      assert(client[kRunning] === 0)\n      // Response completed before request.\n      util.destroy(socket, new InformationalError('reset'))\n      return constants.ERROR.PAUSED\n    } else if (!shouldKeepAlive) {\n      util.destroy(socket, new InformationalError('reset'))\n      return constants.ERROR.PAUSED\n    } else if (socket[kReset] && client[kRunning] === 0) {\n      // Destroy socket once all requests have completed.\n      // The request at the tail of the pipeline is the one\n      // that requested reset and no further requests should\n      // have been queued since then.\n      util.destroy(socket, new InformationalError('reset'))\n      return constants.ERROR.PAUSED\n    } else if (client[kPipelining] == null || client[kPipelining] === 1) {\n      // We must wait a full event loop cycle to reuse this socket to make sure\n      // that non-spec compliant servers are not closing the connection even if they\n      // said they won't.\n      setImmediate(client[kResume])\n    } else {\n      client[kResume]()\n    }\n\n    return 0\n  }\n}\n\nfunction onParserTimeout (parserWeakRef) {\n  const parser = parserWeakRef.deref()\n  if (!parser) {\n    return\n  }\n\n  const { socket, timeoutType, client, paused } = parser\n\n  if (timeoutType === TIMEOUT_HEADERS) {\n    if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {\n      assert(!paused, 'cannot be paused while waiting for headers')\n      util.destroy(socket, new HeadersTimeoutError())\n    }\n  } else if (timeoutType === TIMEOUT_BODY) {\n    if (!paused) {\n      util.destroy(socket, new BodyTimeoutError())\n    }\n  } else if (timeoutType === TIMEOUT_KEEP_ALIVE) {\n    assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue])\n    util.destroy(socket, new InformationalError('socket idle timeout'))\n  }\n}\n\n/**\n * @param {import ('./client.js')} client\n * @param {import('net').Socket} socket\n * @returns\n */\nfunction connectH1 (client, socket) {\n  client[kSocket] = socket\n\n  if (!llhttpInstance) {\n    llhttpInstance = lazyllhttp()\n  }\n\n  if (socket.errored) {\n    throw socket.errored\n  }\n\n  if (socket.destroyed) {\n    throw new SocketError('destroyed')\n  }\n\n  socket[kNoRef] = false\n  socket[kWriting] = false\n  socket[kReset] = false\n  socket[kBlocking] = false\n  socket[kParser] = new Parser(client, socket, llhttpInstance)\n\n  util.addListener(socket, 'error', onHttpSocketError)\n  util.addListener(socket, 'readable', onHttpSocketReadable)\n  util.addListener(socket, 'end', onHttpSocketEnd)\n  util.addListener(socket, 'close', onHttpSocketClose)\n\n  socket[kClosed] = false\n  socket.on('close', onSocketClose)\n\n  return {\n    version: 'h1',\n    defaultPipelining: 1,\n    write (request) {\n      return writeH1(client, request)\n    },\n    resume () {\n      resumeH1(client)\n    },\n    /**\n     * @param {Error|undefined} err\n     * @param {() => void} callback\n     */\n    destroy (err, callback) {\n      if (socket[kClosed]) {\n        queueMicrotask(callback)\n      } else {\n        socket.on('close', callback)\n        socket.destroy(err)\n      }\n    },\n    /**\n     * @returns {boolean}\n     */\n    get destroyed () {\n      return socket.destroyed\n    },\n    /**\n     * @param {import('../core/request.js')} request\n     * @returns {boolean}\n     */\n    busy (request) {\n      if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {\n        return true\n      }\n\n      if (request) {\n        if (client[kRunning] > 0 && !request.idempotent) {\n          // Non-idempotent request cannot be retried.\n          // Ensure that no other requests are inflight and\n          // could cause failure.\n          return true\n        }\n\n        if (client[kRunning] > 0 && (request.upgrade || request.method === 'CONNECT')) {\n          // Don't dispatch an upgrade until all preceding requests have completed.\n          // A misbehaving server might upgrade the connection before all pipelined\n          // request has completed.\n          return true\n        }\n\n        if (client[kRunning] > 0 && util.bodyLength(request.body) !== 0 &&\n          (util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) {\n          // Request with stream or iterator body can error while other requests\n          // are inflight and indirectly error those as well.\n          // Ensure this doesn't happen by waiting for inflight\n          // to complete before dispatching.\n\n          // Request with stream or iterator body cannot be retried.\n          // Ensure that no other requests are inflight and\n          // could cause failure.\n          return true\n        }\n      }\n\n      return false\n    }\n  }\n}\n\nfunction onHttpSocketError (err) {\n  assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')\n\n  const parser = this[kParser]\n\n  // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded\n  // to the user.\n  if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {\n    // We treat all incoming data so for as a valid response.\n    parser.onMessageComplete()\n    return\n  }\n\n  this[kError] = err\n\n  this[kClient][kOnError](err)\n}\n\nfunction onHttpSocketReadable () {\n  this[kParser]?.readMore()\n}\n\nfunction onHttpSocketEnd () {\n  const parser = this[kParser]\n\n  if (parser.statusCode && !parser.shouldKeepAlive) {\n    // We treat all incoming data so far as a valid response.\n    parser.onMessageComplete()\n    return\n  }\n\n  util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))\n}\n\nfunction onHttpSocketClose () {\n  const parser = this[kParser]\n\n  if (parser) {\n    if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {\n      // We treat all incoming data so far as a valid response.\n      parser.onMessageComplete()\n    }\n\n    this[kParser].destroy()\n    this[kParser] = null\n  }\n\n  const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))\n\n  const client = this[kClient]\n\n  client[kSocket] = null\n  client[kHTTPContext] = null // TODO (fix): This is hacky...\n\n  if (client.destroyed) {\n    assert(client[kPending] === 0)\n\n    // Fail entire queue.\n    const requests = client[kQueue].splice(client[kRunningIdx])\n    for (let i = 0; i < requests.length; i++) {\n      const request = requests[i]\n      util.errorRequest(client, request, err)\n    }\n  } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') {\n    // Fail head of pipeline.\n    const request = client[kQueue][client[kRunningIdx]]\n    client[kQueue][client[kRunningIdx]++] = null\n\n    util.errorRequest(client, request, err)\n  }\n\n  client[kPendingIdx] = client[kRunningIdx]\n\n  assert(client[kRunning] === 0)\n\n  client.emit('disconnect', client[kUrl], [client], err)\n\n  client[kResume]()\n}\n\nfunction onSocketClose () {\n  this[kClosed] = true\n}\n\n/**\n * @param {import('./client.js')} client\n */\nfunction resumeH1 (client) {\n  const socket = client[kSocket]\n\n  if (socket && !socket.destroyed) {\n    if (client[kSize] === 0) {\n      if (!socket[kNoRef] && socket.unref) {\n        socket.unref()\n        socket[kNoRef] = true\n      }\n    } else if (socket[kNoRef] && socket.ref) {\n      socket.ref()\n      socket[kNoRef] = false\n    }\n\n    if (client[kSize] === 0) {\n      if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {\n        socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)\n      }\n    } else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) {\n      if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) {\n        const request = client[kQueue][client[kRunningIdx]]\n        const headersTimeout = request.headersTimeout != null\n          ? request.headersTimeout\n          : client[kHeadersTimeout]\n        socket[kParser].setTimeout(headersTimeout, TIMEOUT_HEADERS)\n      }\n    }\n  }\n}\n\n// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2\nfunction shouldSendContentLength (method) {\n  return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT'\n}\n\n/**\n * @param {import('./client.js')} client\n * @param {import('../core/request.js')} request\n * @returns\n */\nfunction writeH1 (client, request) {\n  const { method, path, host, upgrade, blocking, reset } = request\n\n  let { body, headers, contentLength } = request\n\n  // https://tools.ietf.org/html/rfc7231#section-4.3.1\n  // https://tools.ietf.org/html/rfc7231#section-4.3.2\n  // https://tools.ietf.org/html/rfc7231#section-4.3.5\n\n  // Sending a payload body on a request that does not\n  // expect it can cause undefined behavior on some\n  // servers and corrupt connection state. Do not\n  // re-use the connection for further requests.\n\n  const expectsPayload = (\n    method === 'PUT' ||\n    method === 'POST' ||\n    method === 'PATCH' ||\n    method === 'QUERY' ||\n    method === 'PROPFIND' ||\n    method === 'PROPPATCH'\n  )\n\n  if (util.isFormDataLike(body)) {\n    if (!extractBody) {\n      extractBody = require('../web/fetch/body.js').extractBody\n    }\n\n    const [bodyStream, contentType] = extractBody(body)\n    if (request.contentType == null) {\n      headers.push('content-type', contentType)\n    }\n    body = bodyStream.stream\n    contentLength = bodyStream.length\n  } else if (util.isBlobLike(body) && request.contentType == null && body.type) {\n    headers.push('content-type', body.type)\n  }\n\n  if (body && typeof body.read === 'function') {\n    // Try to read EOF in order to get length.\n    body.read(0)\n  }\n\n  const bodyLength = util.bodyLength(body)\n\n  contentLength = bodyLength ?? contentLength\n\n  if (contentLength === null) {\n    contentLength = request.contentLength\n  }\n\n  if (contentLength === 0 && !expectsPayload) {\n    // https://tools.ietf.org/html/rfc7230#section-3.3.2\n    // A user agent SHOULD NOT send a Content-Length header field when\n    // the request message does not contain a payload body and the method\n    // semantics do not anticipate such a body.\n\n    contentLength = null\n  }\n\n  // https://github.com/nodejs/undici/issues/2046\n  // A user agent may send a Content-Length header with 0 value, this should be allowed.\n  if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength !== null && request.contentLength !== contentLength) {\n    if (client[kStrictContentLength]) {\n      util.errorRequest(client, request, new RequestContentLengthMismatchError())\n      return false\n    }\n\n    process.emitWarning(new RequestContentLengthMismatchError())\n  }\n\n  const socket = client[kSocket]\n\n  /**\n   * @param {Error} [err]\n   * @returns {void}\n   */\n  const abort = (err) => {\n    if (request.aborted || request.completed) {\n      return\n    }\n\n    util.errorRequest(client, request, err || new RequestAbortedError())\n\n    util.destroy(body)\n    util.destroy(socket, new InformationalError('aborted'))\n  }\n\n  try {\n    request.onConnect(abort)\n  } catch (err) {\n    util.errorRequest(client, request, err)\n  }\n\n  if (request.aborted) {\n    return false\n  }\n\n  if (method === 'HEAD') {\n    // https://github.com/mcollina/undici/issues/258\n    // Close after a HEAD request to interop with misbehaving servers\n    // that may send a body in the response.\n\n    socket[kReset] = true\n  }\n\n  if (upgrade || method === 'CONNECT') {\n    // On CONNECT or upgrade, block pipeline from dispatching further\n    // requests on this connection.\n\n    socket[kReset] = true\n  }\n\n  if (reset != null) {\n    socket[kReset] = reset\n  }\n\n  if (client[kMaxRequests] && socket[kCounter]++ >= client[kMaxRequests]) {\n    socket[kReset] = true\n  }\n\n  if (blocking) {\n    socket[kBlocking] = true\n  }\n\n  if (socket.setTypeOfService) {\n    socket.setTypeOfService(request.typeOfService)\n  }\n\n  let header = `${method} ${path} HTTP/1.1\\r\\n`\n\n  if (typeof host === 'string') {\n    header += `host: ${host}\\r\\n`\n  } else {\n    header += client[kHostHeader]\n  }\n\n  if (upgrade) {\n    header += `connection: upgrade\\r\\nupgrade: ${upgrade}\\r\\n`\n  } else if (client[kPipelining] && !socket[kReset]) {\n    header += 'connection: keep-alive\\r\\n'\n  } else {\n    header += 'connection: close\\r\\n'\n  }\n\n  if (Array.isArray(headers)) {\n    for (let n = 0; n < headers.length; n += 2) {\n      const key = headers[n + 0]\n      const val = headers[n + 1]\n\n      if (Array.isArray(val)) {\n        for (let i = 0; i < val.length; i++) {\n          header += `${key}: ${val[i]}\\r\\n`\n        }\n      } else {\n        header += `${key}: ${val}\\r\\n`\n      }\n    }\n  }\n\n  if (channels.sendHeaders.hasSubscribers) {\n    channels.sendHeaders.publish({ request, headers: header, socket })\n  }\n\n  if (!body || bodyLength === 0) {\n    writeBuffer(abort, null, client, request, socket, contentLength, header, expectsPayload)\n  } else if (util.isBuffer(body)) {\n    writeBuffer(abort, body, client, request, socket, contentLength, header, expectsPayload)\n  } else if (util.isBlobLike(body)) {\n    if (typeof body.stream === 'function') {\n      writeIterable(abort, body.stream(), client, request, socket, contentLength, header, expectsPayload)\n    } else {\n      writeBlob(abort, body, client, request, socket, contentLength, header, expectsPayload)\n    }\n  } else if (util.isStream(body)) {\n    writeStream(abort, body, client, request, socket, contentLength, header, expectsPayload)\n  } else if (util.isIterable(body)) {\n    writeIterable(abort, body, client, request, socket, contentLength, header, expectsPayload)\n  } else {\n    assert(false)\n  }\n\n  return true\n}\n\n/**\n * @param {AbortCallback} abort\n * @param {import('stream').Stream} body\n * @param {import('./client.js')} client\n * @param {import('../core/request.js')} request\n * @param {import('net').Socket} socket\n * @param {number} contentLength\n * @param {string} header\n * @param {boolean} expectsPayload\n */\nfunction writeStream (abort, body, client, request, socket, contentLength, header, expectsPayload) {\n  assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined')\n\n  let finished = false\n\n  const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })\n\n  /**\n   * @param {Buffer} chunk\n   * @returns {void}\n   */\n  const onData = function (chunk) {\n    if (finished) {\n      return\n    }\n\n    try {\n      if (!writer.write(chunk) && this.pause) {\n        this.pause()\n      }\n    } catch (err) {\n      util.destroy(this, err)\n    }\n  }\n\n  /**\n   * @returns {void}\n   */\n  const onDrain = function () {\n    if (finished) {\n      return\n    }\n\n    if (body.resume) {\n      body.resume()\n    }\n  }\n\n  /**\n   * @returns {void}\n   */\n  const onClose = function () {\n    // 'close' might be emitted *before* 'error' for\n    // broken streams. Wait a tick to avoid this case.\n    queueMicrotask(() => {\n      // It's only safe to remove 'error' listener after\n      // 'close'.\n      body.removeListener('error', onFinished)\n    })\n\n    if (!finished) {\n      const err = new RequestAbortedError()\n      queueMicrotask(() => onFinished(err))\n    }\n  }\n\n  /**\n   * @param {Error} [err]\n   * @returns\n   */\n  const onFinished = function (err) {\n    if (finished) {\n      return\n    }\n\n    finished = true\n\n    assert(socket.destroyed || (socket[kWriting] && client[kRunning] <= 1))\n\n    socket\n      .off('drain', onDrain)\n      .off('error', onFinished)\n\n    body\n      .removeListener('data', onData)\n      .removeListener('end', onFinished)\n      .removeListener('close', onClose)\n\n    if (!err) {\n      try {\n        writer.end()\n      } catch (er) {\n        err = er\n      }\n    }\n\n    writer.destroy(err)\n\n    if (err && (err.code !== 'UND_ERR_INFO' || err.message !== 'reset')) {\n      util.destroy(body, err)\n    } else {\n      util.destroy(body)\n    }\n  }\n\n  body\n    .on('data', onData)\n    .on('end', onFinished)\n    .on('error', onFinished)\n    .on('close', onClose)\n\n  if (body.resume) {\n    body.resume()\n  }\n\n  socket\n    .on('drain', onDrain)\n    .on('error', onFinished)\n\n  if (body.errorEmitted ?? body.errored) {\n    setImmediate(onFinished, body.errored)\n  } else if (body.endEmitted ?? body.readableEnded) {\n    setImmediate(onFinished, null)\n  }\n\n  if (body.closeEmitted ?? body.closed) {\n    setImmediate(onClose)\n  }\n}\n\n/**\n * @typedef AbortCallback\n * @type {Function}\n * @param {Error} [err]\n * @returns {void}\n */\n\n/**\n * @param {AbortCallback} abort\n * @param {Uint8Array|null} body\n * @param {import('./client.js')} client\n * @param {import('../core/request.js')} request\n * @param {import('net').Socket} socket\n * @param {number} contentLength\n * @param {string} header\n * @param {boolean} expectsPayload\n * @returns {void}\n */\nfunction writeBuffer (abort, body, client, request, socket, contentLength, header, expectsPayload) {\n  try {\n    if (!body) {\n      if (contentLength === 0) {\n        socket.write(`${header}content-length: 0\\r\\n\\r\\n`, 'latin1')\n      } else {\n        assert(contentLength === null, 'no body must not have content length')\n        socket.write(`${header}\\r\\n`, 'latin1')\n      }\n    } else if (util.isBuffer(body)) {\n      assert(contentLength === body.byteLength, 'buffer body must have content length')\n\n      socket.cork()\n      socket.write(`${header}content-length: ${contentLength}\\r\\n\\r\\n`, 'latin1')\n      socket.write(body)\n      socket.uncork()\n      request.onBodySent(body)\n\n      if (!expectsPayload && request.reset !== false) {\n        socket[kReset] = true\n      }\n    }\n    request.onRequestSent()\n\n    client[kResume]()\n  } catch (err) {\n    abort(err)\n  }\n}\n\n/**\n * @param {AbortCallback} abort\n * @param {Blob} body\n * @param {import('./client.js')} client\n * @param {import('../core/request.js')} request\n * @param {import('net').Socket} socket\n * @param {number} contentLength\n * @param {string} header\n * @param {boolean} expectsPayload\n * @returns {Promise<void>}\n */\nasync function writeBlob (abort, body, client, request, socket, contentLength, header, expectsPayload) {\n  assert(contentLength === body.size, 'blob body must have content length')\n\n  try {\n    if (contentLength != null && contentLength !== body.size) {\n      throw new RequestContentLengthMismatchError()\n    }\n\n    const buffer = Buffer.from(await body.arrayBuffer())\n\n    socket.cork()\n    socket.write(`${header}content-length: ${contentLength}\\r\\n\\r\\n`, 'latin1')\n    socket.write(buffer)\n    socket.uncork()\n\n    request.onBodySent(buffer)\n    request.onRequestSent()\n\n    if (!expectsPayload && request.reset !== false) {\n      socket[kReset] = true\n    }\n\n    client[kResume]()\n  } catch (err) {\n    abort(err)\n  }\n}\n\n/**\n * @param {AbortCallback} abort\n * @param {Iterable} body\n * @param {import('./client.js')} client\n * @param {import('../core/request.js')} request\n * @param {import('net').Socket} socket\n * @param {number} contentLength\n * @param {string} header\n * @param {boolean} expectsPayload\n * @returns {Promise<void>}\n */\nasync function writeIterable (abort, body, client, request, socket, contentLength, header, expectsPayload) {\n  assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined')\n\n  let callback = null\n  function onDrain () {\n    if (callback) {\n      const cb = callback\n      callback = null\n      cb()\n    }\n  }\n\n  const waitForDrain = () => new Promise((resolve, reject) => {\n    assert(callback === null)\n\n    if (socket[kError]) {\n      reject(socket[kError])\n    } else {\n      callback = resolve\n    }\n  })\n\n  socket\n    .on('close', onDrain)\n    .on('drain', onDrain)\n\n  const writer = new AsyncWriter({ abort, socket, request, contentLength, client, expectsPayload, header })\n  try {\n    // It's up to the user to somehow abort the async iterable.\n    for await (const chunk of body) {\n      if (socket[kError]) {\n        throw socket[kError]\n      }\n\n      if (!writer.write(chunk)) {\n        await waitForDrain()\n      }\n    }\n\n    writer.end()\n  } catch (err) {\n    writer.destroy(err)\n  } finally {\n    socket\n      .off('close', onDrain)\n      .off('drain', onDrain)\n  }\n}\n\nclass AsyncWriter {\n  /**\n   *\n   * @param {object} arg\n   * @param {AbortCallback} arg.abort\n   * @param {import('net').Socket} arg.socket\n   * @param {import('../core/request.js')} arg.request\n   * @param {number} arg.contentLength\n   * @param {import('./client.js')} arg.client\n   * @param {boolean} arg.expectsPayload\n   * @param {string} arg.header\n   */\n  constructor ({ abort, socket, request, contentLength, client, expectsPayload, header }) {\n    this.socket = socket\n    this.request = request\n    this.contentLength = contentLength\n    this.client = client\n    this.bytesWritten = 0\n    this.expectsPayload = expectsPayload\n    this.header = header\n    this.abort = abort\n\n    socket[kWriting] = true\n  }\n\n  /**\n   * @param {Buffer} chunk\n   * @returns\n   */\n  write (chunk) {\n    const { socket, request, contentLength, client, bytesWritten, expectsPayload, header } = this\n\n    if (socket[kError]) {\n      throw socket[kError]\n    }\n\n    if (socket.destroyed) {\n      return false\n    }\n\n    const len = Buffer.byteLength(chunk)\n    if (!len) {\n      return true\n    }\n\n    // We should defer writing chunks.\n    if (contentLength !== null && bytesWritten + len > contentLength) {\n      if (client[kStrictContentLength]) {\n        throw new RequestContentLengthMismatchError()\n      }\n\n      process.emitWarning(new RequestContentLengthMismatchError())\n    }\n\n    socket.cork()\n\n    if (bytesWritten === 0) {\n      if (!expectsPayload && request.reset !== false) {\n        socket[kReset] = true\n      }\n\n      if (contentLength === null) {\n        socket.write(`${header}transfer-encoding: chunked\\r\\n`, 'latin1')\n      } else {\n        socket.write(`${header}content-length: ${contentLength}\\r\\n\\r\\n`, 'latin1')\n      }\n    }\n\n    if (contentLength === null) {\n      socket.write(`\\r\\n${len.toString(16)}\\r\\n`, 'latin1')\n    }\n\n    this.bytesWritten += len\n\n    const ret = socket.write(chunk)\n\n    socket.uncork()\n\n    request.onBodySent(chunk)\n\n    if (!ret) {\n      if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {\n        if (socket[kParser].timeout.refresh) {\n          socket[kParser].timeout.refresh()\n        }\n      }\n    }\n\n    return ret\n  }\n\n  /**\n   * @returns {void}\n   */\n  end () {\n    const { socket, contentLength, client, bytesWritten, expectsPayload, header, request } = this\n    request.onRequestSent()\n\n    socket[kWriting] = false\n\n    if (socket[kError]) {\n      throw socket[kError]\n    }\n\n    if (socket.destroyed) {\n      return\n    }\n\n    if (bytesWritten === 0) {\n      if (expectsPayload) {\n        // https://tools.ietf.org/html/rfc7230#section-3.3.2\n        // A user agent SHOULD send a Content-Length in a request message when\n        // no Transfer-Encoding is sent and the request method defines a meaning\n        // for an enclosed payload body.\n\n        socket.write(`${header}content-length: 0\\r\\n\\r\\n`, 'latin1')\n      } else {\n        socket.write(`${header}\\r\\n`, 'latin1')\n      }\n    } else if (contentLength === null) {\n      socket.write('\\r\\n0\\r\\n\\r\\n', 'latin1')\n    }\n\n    if (contentLength !== null && bytesWritten !== contentLength) {\n      if (client[kStrictContentLength]) {\n        throw new RequestContentLengthMismatchError()\n      } else {\n        process.emitWarning(new RequestContentLengthMismatchError())\n      }\n    }\n\n    if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {\n      if (socket[kParser].timeout.refresh) {\n        socket[kParser].timeout.refresh()\n      }\n    }\n\n    client[kResume]()\n  }\n\n  /**\n   * @param {Error} [err]\n   * @returns {void}\n   */\n  destroy (err) {\n    const { socket, client, abort } = this\n\n    socket[kWriting] = false\n\n    if (err) {\n      assert(client[kRunning] <= 1, 'pipeline should only contain this request')\n      abort(err)\n    }\n  }\n}\n\nmodule.exports = connectH1\n"
  },
  {
    "path": "lib/dispatcher/client-h2.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { pipeline } = require('node:stream')\nconst util = require('../core/util.js')\nconst {\n  RequestContentLengthMismatchError,\n  RequestAbortedError,\n  SocketError,\n  InformationalError,\n  InvalidArgumentError\n} = require('../core/errors.js')\nconst {\n  kUrl,\n  kReset,\n  kClient,\n  kRunning,\n  kPending,\n  kQueue,\n  kPendingIdx,\n  kRunningIdx,\n  kError,\n  kSocket,\n  kStrictContentLength,\n  kOnError,\n  kMaxConcurrentStreams,\n  kPingInterval,\n  kHTTP2Session,\n  kHTTP2InitialWindowSize,\n  kHTTP2ConnectionWindowSize,\n  kResume,\n  kSize,\n  kHTTPContext,\n  kClosed,\n  kBodyTimeout,\n  kEnableConnectProtocol,\n  kRemoteSettings,\n  kHTTP2Stream,\n  kHTTP2SessionState\n} = require('../core/symbols.js')\nconst { channels } = require('../core/diagnostics.js')\n\nconst kOpenStreams = Symbol('open streams')\n\nlet extractBody\n\n/** @type {import('http2')} */\nlet http2\ntry {\n  http2 = require('node:http2')\n} catch {\n  // @ts-ignore\n  http2 = { constants: {} }\n}\n\nconst {\n  constants: {\n    HTTP2_HEADER_AUTHORITY,\n    HTTP2_HEADER_METHOD,\n    HTTP2_HEADER_PATH,\n    HTTP2_HEADER_SCHEME,\n    HTTP2_HEADER_CONTENT_LENGTH,\n    HTTP2_HEADER_EXPECT,\n    HTTP2_HEADER_STATUS,\n    HTTP2_HEADER_PROTOCOL,\n    NGHTTP2_REFUSED_STREAM,\n    NGHTTP2_CANCEL\n  }\n} = http2\n\nfunction parseH2Headers (headers) {\n  const result = []\n\n  for (const [name, value] of Object.entries(headers)) {\n    // h2 may concat the header value by array\n    // e.g. Set-Cookie\n    if (Array.isArray(value)) {\n      for (const subvalue of value) {\n        // we need to provide each header value of header name\n        // because the headers handler expect name-value pair\n        result.push(Buffer.from(name), Buffer.from(subvalue))\n      }\n    } else {\n      result.push(Buffer.from(name), Buffer.from(value))\n    }\n  }\n\n  return result\n}\n\nfunction connectH2 (client, socket) {\n  client[kSocket] = socket\n\n  const http2InitialWindowSize = client[kHTTP2InitialWindowSize]\n  const http2ConnectionWindowSize = client[kHTTP2ConnectionWindowSize]\n\n  const session = http2.connect(client[kUrl], {\n    createConnection: () => socket,\n    peerMaxConcurrentStreams: client[kMaxConcurrentStreams],\n    settings: {\n      // TODO(metcoder95): add support for PUSH\n      enablePush: false,\n      ...(http2InitialWindowSize != null ? { initialWindowSize: http2InitialWindowSize } : null)\n    }\n  })\n\n  client[kSocket] = socket\n  session[kOpenStreams] = 0\n  session[kClient] = client\n  session[kSocket] = socket\n  session[kHTTP2SessionState] = {\n    ping: {\n      interval: client[kPingInterval] === 0 ? null : setInterval(onHttp2SendPing, client[kPingInterval], session).unref()\n    }\n  }\n  // We set it to true by default in a best-effort; however once connected to an H2 server\n  // we will check if extended CONNECT protocol is supported or not\n  // and set this value accordingly.\n  session[kEnableConnectProtocol] = false\n  // States whether or not we have received the remote settings from the server\n  session[kRemoteSettings] = false\n\n  // Apply connection-level flow control once connected (if supported).\n  if (http2ConnectionWindowSize) {\n    util.addListener(session, 'connect', applyConnectionWindowSize.bind(session, http2ConnectionWindowSize))\n  }\n\n  util.addListener(session, 'error', onHttp2SessionError)\n  util.addListener(session, 'frameError', onHttp2FrameError)\n  util.addListener(session, 'end', onHttp2SessionEnd)\n  util.addListener(session, 'goaway', onHttp2SessionGoAway)\n  util.addListener(session, 'close', onHttp2SessionClose)\n  util.addListener(session, 'remoteSettings', onHttp2RemoteSettings)\n  // TODO (@metcoder95): implement SETTINGS support\n  // util.addListener(session, 'localSettings', onHttp2RemoteSettings)\n\n  session.unref()\n\n  client[kHTTP2Session] = session\n  socket[kHTTP2Session] = session\n\n  util.addListener(socket, 'error', onHttp2SocketError)\n  util.addListener(socket, 'end', onHttp2SocketEnd)\n  util.addListener(socket, 'close', onHttp2SocketClose)\n\n  socket[kClosed] = false\n  socket.on('close', onSocketClose)\n\n  return {\n    version: 'h2',\n    defaultPipelining: Infinity,\n    /**\n     * @param {import('../core/request.js')} request\n     * @returns {boolean}\n    */\n    write (request) {\n      return writeH2(client, request)\n    },\n    /**\n     * @returns {void}\n     */\n    resume () {\n      resumeH2(client)\n    },\n    /**\n     * @param {Error | null} err\n     * @param {() => void} callback\n     */\n    destroy (err, callback) {\n      if (socket[kClosed]) {\n        queueMicrotask(callback)\n      } else {\n        socket.destroy(err).on('close', callback)\n      }\n    },\n    /**\n     * @type {boolean}\n     */\n    get destroyed () {\n      return socket.destroyed\n    },\n    /**\n     * @param {import('../core/request.js')} request\n     * @returns {boolean}\n    */\n    busy (request) {\n      if (request != null) {\n        if (client[kRunning] > 0) {\n          // We are already processing requests\n\n          // Non-idempotent request cannot be retried.\n          // Ensure that no other requests are inflight and\n          // could cause failure.\n          if (request.idempotent === false) return true\n          // Don't dispatch an upgrade until all preceding requests have completed.\n          // Possibly, we do not have remote settings confirmed yet.\n          if ((request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false) return true\n          // Request with stream or iterator body can error while other requests\n          // are inflight and indirectly error those as well.\n          // Ensure this doesn't happen by waiting for inflight\n          // to complete before dispatching.\n\n          // Request with stream or iterator body cannot be retried.\n          // Ensure that no other requests are inflight and\n          // could cause failure.\n          if (util.bodyLength(request.body) !== 0 &&\n            (util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) return true\n        } else {\n          return (request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false\n        }\n      }\n\n      return false\n    }\n  }\n}\n\nfunction resumeH2 (client) {\n  const socket = client[kSocket]\n\n  if (socket?.destroyed === false) {\n    if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) {\n      socket.unref()\n      client[kHTTP2Session].unref()\n    } else {\n      socket.ref()\n      client[kHTTP2Session].ref()\n    }\n  }\n}\n\nfunction applyConnectionWindowSize (connectionWindowSize) {\n  try {\n    if (typeof this.setLocalWindowSize === 'function') {\n      this.setLocalWindowSize(connectionWindowSize)\n    }\n  } catch {\n    // Best-effort only.\n  }\n}\n\nfunction onHttp2RemoteSettings (settings) {\n  // Fallbacks are a safe bet, remote setting will always override\n  this[kClient][kMaxConcurrentStreams] = settings.maxConcurrentStreams ?? this[kClient][kMaxConcurrentStreams]\n  /**\n   * From RFC-8441\n   * A sender MUST NOT send a SETTINGS_ENABLE_CONNECT_PROTOCOL parameter\n   * with the value of 0 after previously sending a value of 1.\n   */\n  // Note: Cannot be tested in Node, it does not supports disabling the extended CONNECT protocol once enabled\n  if (this[kRemoteSettings] === true && this[kEnableConnectProtocol] === true && settings.enableConnectProtocol === false) {\n    const err = new InformationalError('HTTP/2: Server disabled extended CONNECT protocol against RFC-8441')\n    this[kSocket][kError] = err\n    this[kClient][kOnError](err)\n    return\n  }\n\n  this[kEnableConnectProtocol] = settings.enableConnectProtocol ?? this[kEnableConnectProtocol]\n  this[kRemoteSettings] = true\n  this[kClient][kResume]()\n}\n\nfunction onHttp2SendPing (session) {\n  const state = session[kHTTP2SessionState]\n  if ((session.closed || session.destroyed) && state.ping.interval != null) {\n    clearInterval(state.ping.interval)\n    state.ping.interval = null\n    return\n  }\n\n  // If no ping sent, do nothing\n  session.ping(onPing.bind(session))\n\n  function onPing (err, duration) {\n    const client = this[kClient]\n    const socket = this[kClient]\n\n    if (err != null) {\n      const error = new InformationalError(`HTTP/2: \"PING\" errored - type ${err.message}`)\n      socket[kError] = error\n      client[kOnError](error)\n    } else {\n      client.emit('ping', duration)\n    }\n  }\n}\n\nfunction onHttp2SessionError (err) {\n  assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')\n\n  this[kSocket][kError] = err\n  this[kClient][kOnError](err)\n}\n\nfunction onHttp2FrameError (type, code, id) {\n  if (id === 0) {\n    const err = new InformationalError(`HTTP/2: \"frameError\" received - type ${type}, code ${code}`)\n    this[kSocket][kError] = err\n    this[kClient][kOnError](err)\n  }\n}\n\nfunction onHttp2SessionEnd () {\n  const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket]))\n  this.destroy(err)\n  util.destroy(this[kSocket], err)\n}\n\n/**\n * This is the root cause of #3011\n * We need to handle GOAWAY frames properly, and trigger the session close\n * along with the socket right away\n *\n * @this {import('http2').ClientHttp2Session}\n * @param {number} errorCode\n */\nfunction onHttp2SessionGoAway (errorCode) {\n  // TODO(mcollina): Verify if GOAWAY implements the spec correctly:\n  // https://datatracker.ietf.org/doc/html/rfc7540#section-6.8\n  // Specifically, we do not verify the \"valid\" stream id.\n\n  const err = this[kError] || new SocketError(`HTTP/2: \"GOAWAY\" frame received with code ${errorCode}`, util.getSocketInfo(this[kSocket]))\n  const client = this[kClient]\n\n  client[kSocket] = null\n  client[kHTTPContext] = null\n\n  // this is an HTTP2 session\n  this.close()\n  this[kHTTP2Session] = null\n\n  util.destroy(this[kSocket], err)\n\n  // Fail head of pipeline.\n  if (client[kRunningIdx] < client[kQueue].length) {\n    const request = client[kQueue][client[kRunningIdx]]\n    client[kQueue][client[kRunningIdx]++] = null\n    util.errorRequest(client, request, err)\n    client[kPendingIdx] = client[kRunningIdx]\n  }\n\n  assert(client[kRunning] === 0)\n\n  client.emit('disconnect', client[kUrl], [client], err)\n  client.emit('connectionError', client[kUrl], [client], err)\n\n  client[kResume]()\n}\n\nfunction onHttp2SessionClose () {\n  const { [kClient]: client, [kHTTP2SessionState]: state } = this\n  const { [kSocket]: socket } = client\n\n  const err = this[kSocket][kError] || this[kError] || new SocketError('closed', util.getSocketInfo(socket))\n\n  client[kSocket] = null\n  client[kHTTPContext] = null\n\n  if (state.ping.interval != null) {\n    clearInterval(state.ping.interval)\n    state.ping.interval = null\n  }\n\n  if (client.destroyed) {\n    assert(client[kPending] === 0)\n\n    // Fail entire queue.\n    const requests = client[kQueue].splice(client[kRunningIdx])\n    for (let i = 0; i < requests.length; i++) {\n      const request = requests[i]\n      util.errorRequest(client, request, err)\n    }\n  }\n}\n\nfunction onHttp2SocketClose () {\n  const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))\n\n  const client = this[kHTTP2Session][kClient]\n\n  client[kSocket] = null\n  client[kHTTPContext] = null\n\n  if (this[kHTTP2Session] !== null) {\n    this[kHTTP2Session].destroy(err)\n  }\n\n  client[kPendingIdx] = client[kRunningIdx]\n\n  assert(client[kRunning] === 0)\n\n  client.emit('disconnect', client[kUrl], [client], err)\n\n  client[kResume]()\n}\n\nfunction onHttp2SocketError (err) {\n  assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')\n\n  this[kError] = err\n\n  this[kClient][kOnError](err)\n}\n\nfunction onHttp2SocketEnd () {\n  util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))\n}\n\nfunction onSocketClose () {\n  this[kClosed] = true\n}\n\n// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2\nfunction shouldSendContentLength (method) {\n  return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT'\n}\n\nfunction writeH2 (client, request) {\n  const requestTimeout = request.bodyTimeout ?? client[kBodyTimeout]\n  const session = client[kHTTP2Session]\n  const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request\n  let { body } = request\n\n  if (upgrade != null && upgrade !== 'websocket') {\n    util.errorRequest(client, request, new InvalidArgumentError(`Custom upgrade \"${upgrade}\" not supported over HTTP/2`))\n    return false\n  }\n\n  const headers = {}\n  for (let n = 0; n < reqHeaders.length; n += 2) {\n    const key = reqHeaders[n + 0]\n    const val = reqHeaders[n + 1]\n\n    if (key === 'cookie') {\n      if (headers[key] != null) {\n        headers[key] = Array.isArray(headers[key]) ? (headers[key].push(val), headers[key]) : [headers[key], val]\n      } else {\n        headers[key] = val\n      }\n\n      continue\n    }\n\n    if (Array.isArray(val)) {\n      for (let i = 0; i < val.length; i++) {\n        if (headers[key]) {\n          headers[key] += `, ${val[i]}`\n        } else {\n          headers[key] = val[i]\n        }\n      }\n    } else if (headers[key]) {\n      headers[key] += `, ${val}`\n    } else {\n      headers[key] = val\n    }\n  }\n\n  /** @type {import('node:http2').ClientHttp2Stream} */\n  let stream = null\n\n  const { hostname, port } = client[kUrl]\n\n  headers[HTTP2_HEADER_AUTHORITY] = host || `${hostname}${port ? `:${port}` : ''}`\n  headers[HTTP2_HEADER_METHOD] = method\n\n  const abort = (err) => {\n    if (request.aborted || request.completed) {\n      return\n    }\n\n    err = err || new RequestAbortedError()\n\n    util.errorRequest(client, request, err)\n\n    if (stream != null) {\n      // Some chunks might still come after abort,\n      // let's ignore them\n      stream.removeAllListeners('data')\n\n      // On Abort, we close the stream to send RST_STREAM frame\n      stream.close()\n\n      // We move the running index to the next request\n      client[kOnError](err)\n      client[kResume]()\n    }\n\n    // We do not destroy the socket as we can continue using the session\n    // the stream gets destroyed and the session remains to create new streams\n    util.destroy(body, err)\n  }\n\n  try {\n    // We are already connected, streams are pending.\n    // We can call on connect, and wait for abort\n    request.onConnect(abort)\n  } catch (err) {\n    util.errorRequest(client, request, err)\n  }\n\n  if (request.aborted) {\n    return false\n  }\n\n  if (upgrade || method === 'CONNECT') {\n    session.ref()\n\n    if (upgrade === 'websocket') {\n      // We cannot upgrade to websocket if extended CONNECT protocol is not supported\n      if (session[kEnableConnectProtocol] === false) {\n        util.errorRequest(client, request, new InformationalError('HTTP/2: Extended CONNECT protocol not supported by server'))\n        session.unref()\n        return false\n      }\n\n      // We force the method to CONNECT\n      // as per RFC-8441\n      // https://datatracker.ietf.org/doc/html/rfc8441#section-4\n      headers[HTTP2_HEADER_METHOD] = 'CONNECT'\n      headers[HTTP2_HEADER_PROTOCOL] = 'websocket'\n      // :path and :scheme headers must be omitted when sending CONNECT but set if extended-CONNECT\n      headers[HTTP2_HEADER_PATH] = path\n\n      if (protocol === 'ws:' || protocol === 'wss:') {\n        headers[HTTP2_HEADER_SCHEME] = protocol === 'ws:' ? 'http' : 'https'\n      } else {\n        headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'\n      }\n\n      stream = session.request(headers, { endStream: false, signal })\n      stream[kHTTP2Stream] = true\n\n      stream.once('response', (headers, _flags) => {\n        const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers\n\n        request.onUpgrade(statusCode, parseH2Headers(realHeaders), stream)\n\n        ++session[kOpenStreams]\n        client[kQueue][client[kRunningIdx]++] = null\n      })\n\n      stream.on('error', () => {\n        if (stream.rstCode === NGHTTP2_REFUSED_STREAM || stream.rstCode === NGHTTP2_CANCEL) {\n          // NGHTTP2_REFUSED_STREAM (7) or NGHTTP2_CANCEL (8)\n          // We do not treat those as errors as the server might\n          // not support websockets and refuse the stream\n          abort(new InformationalError(`HTTP/2: \"stream error\" received - code ${stream.rstCode}`))\n        }\n      })\n\n      stream.once('close', () => {\n        session[kOpenStreams] -= 1\n        if (session[kOpenStreams] === 0) session.unref()\n      })\n\n      stream.setTimeout(requestTimeout)\n      return true\n    }\n\n    // TODO: consolidate once we support CONNECT properly\n    // NOTE: We are already connected, streams are pending, first request\n    // will create a new stream. We trigger a request to create the stream and wait until\n    // `ready` event is triggered\n    // We disabled endStream to allow the user to write to the stream\n    stream = session.request(headers, { endStream: false, signal })\n    stream[kHTTP2Stream] = true\n    stream.on('response', headers => {\n      const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers\n\n      request.onUpgrade(statusCode, parseH2Headers(realHeaders), stream)\n      ++session[kOpenStreams]\n      client[kQueue][client[kRunningIdx]++] = null\n    })\n    stream.once('close', () => {\n      session[kOpenStreams] -= 1\n      if (session[kOpenStreams] === 0) session.unref()\n    })\n    stream.setTimeout(requestTimeout)\n\n    return true\n  }\n\n  // https://tools.ietf.org/html/rfc7540#section-8.3\n  // :path and :scheme headers must be omitted when sending CONNECT\n  headers[HTTP2_HEADER_PATH] = path\n  headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'\n\n  // https://tools.ietf.org/html/rfc7231#section-4.3.1\n  // https://tools.ietf.org/html/rfc7231#section-4.3.2\n  // https://tools.ietf.org/html/rfc7231#section-4.3.5\n\n  // Sending a payload body on a request that does not\n  // expect it can cause undefined behavior on some\n  // servers and corrupt connection state. Do not\n  // re-use the connection for further requests.\n\n  const expectsPayload = (\n    method === 'PUT' ||\n    method === 'POST' ||\n    method === 'PATCH'\n  )\n\n  if (body && typeof body.read === 'function') {\n    // Try to read EOF in order to get length.\n    body.read(0)\n  }\n\n  let contentLength = util.bodyLength(body)\n\n  if (util.isFormDataLike(body)) {\n    extractBody ??= require('../web/fetch/body.js').extractBody\n\n    const [bodyStream, contentType] = extractBody(body)\n    headers['content-type'] = contentType\n\n    body = bodyStream.stream\n    contentLength = bodyStream.length\n  }\n\n  if (contentLength == null) {\n    contentLength = request.contentLength\n  }\n\n  if (!expectsPayload) {\n    // https://tools.ietf.org/html/rfc7230#section-3.3.2\n    // A user agent SHOULD NOT send a Content-Length header field when\n    // the request message does not contain a payload body and the method\n    // semantics do not anticipate such a body.\n    // And for methods that don't expect a payload, omit Content-Length.\n    contentLength = null\n  }\n\n  // https://github.com/nodejs/undici/issues/2046\n  // A user agent may send a Content-Length header with 0 value, this should be allowed.\n  if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength != null && request.contentLength !== contentLength) {\n    if (client[kStrictContentLength]) {\n      util.errorRequest(client, request, new RequestContentLengthMismatchError())\n      return false\n    }\n\n    process.emitWarning(new RequestContentLengthMismatchError())\n  }\n\n  if (contentLength != null) {\n    assert(body || contentLength === 0, 'no body must not have content length')\n    headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`\n  }\n\n  session.ref()\n\n  if (channels.sendHeaders.hasSubscribers) {\n    let header = ''\n    for (const key in headers) {\n      header += `${key}: ${headers[key]}\\r\\n`\n    }\n    channels.sendHeaders.publish({ request, headers: header, socket: session[kSocket] })\n  }\n\n  // TODO(metcoder95): add support for sending trailers\n  const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null\n  if (expectContinue) {\n    headers[HTTP2_HEADER_EXPECT] = '100-continue'\n    stream = session.request(headers, { endStream: shouldEndStream, signal })\n    stream[kHTTP2Stream] = true\n\n    stream.once('continue', writeBodyH2)\n  } else {\n    stream = session.request(headers, {\n      endStream: shouldEndStream,\n      signal\n    })\n    stream[kHTTP2Stream] = true\n\n    writeBodyH2()\n  }\n\n  // Increment counter as we have new streams open\n  ++session[kOpenStreams]\n  stream.setTimeout(requestTimeout)\n\n  // Track whether we received a response (headers)\n  let responseReceived = false\n\n  stream.once('response', headers => {\n    const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers\n    request.onResponseStarted()\n    responseReceived = true\n\n    // Due to the stream nature, it is possible we face a race condition\n    // where the stream has been assigned, but the request has been aborted\n    // the request remains in-flight and headers hasn't been received yet\n    // for those scenarios, best effort is to destroy the stream immediately\n    // as there's no value to keep it open.\n    if (request.aborted) {\n      stream.removeAllListeners('data')\n      return\n    }\n\n    if (request.onHeaders(Number(statusCode), parseH2Headers(realHeaders), stream.resume.bind(stream), '') === false) {\n      stream.pause()\n    }\n\n    stream.on('data', (chunk) => {\n      if (request.aborted || request.completed) {\n        return\n      }\n\n      if (request.onData(chunk) === false) {\n        stream.pause()\n      }\n    })\n  })\n\n  stream.once('end', () => {\n    stream.removeAllListeners('data')\n    // If we received a response, this is a normal completion\n    if (responseReceived) {\n      if (!request.aborted && !request.completed) {\n        request.onComplete({})\n      }\n\n      client[kQueue][client[kRunningIdx]++] = null\n      client[kResume]()\n    } else {\n      // Stream ended without receiving a response - this is an error\n      // (e.g., server destroyed the stream before sending headers)\n      abort(new InformationalError('HTTP/2: stream half-closed (remote)'))\n      client[kQueue][client[kRunningIdx]++] = null\n      client[kPendingIdx] = client[kRunningIdx]\n      client[kResume]()\n    }\n  })\n\n  stream.once('close', () => {\n    stream.removeAllListeners('data')\n    session[kOpenStreams] -= 1\n    if (session[kOpenStreams] === 0) {\n      session.unref()\n    }\n  })\n\n  stream.once('error', function (err) {\n    stream.removeAllListeners('data')\n    abort(err)\n  })\n\n  stream.once('frameError', (type, code) => {\n    stream.removeAllListeners('data')\n    abort(new InformationalError(`HTTP/2: \"frameError\" received - type ${type}, code ${code}`))\n  })\n\n  stream.on('aborted', () => {\n    stream.removeAllListeners('data')\n  })\n\n  stream.on('timeout', () => {\n    const err = new InformationalError(`HTTP/2: \"stream timeout after ${requestTimeout}\"`)\n    stream.removeAllListeners('data')\n    session[kOpenStreams] -= 1\n\n    if (session[kOpenStreams] === 0) {\n      session.unref()\n    }\n\n    abort(err)\n  })\n\n  stream.once('trailers', trailers => {\n    if (request.aborted || request.completed) {\n      return\n    }\n\n    stream.removeAllListeners('data')\n    request.onComplete(trailers)\n  })\n\n  return true\n\n  function writeBodyH2 () {\n    if (!body || contentLength === 0) {\n      writeBuffer(\n        abort,\n        stream,\n        null,\n        client,\n        request,\n        client[kSocket],\n        contentLength,\n        expectsPayload\n      )\n    } else if (util.isBuffer(body)) {\n      writeBuffer(\n        abort,\n        stream,\n        body,\n        client,\n        request,\n        client[kSocket],\n        contentLength,\n        expectsPayload\n      )\n    } else if (util.isBlobLike(body)) {\n      if (typeof body.stream === 'function') {\n        writeIterable(\n          abort,\n          stream,\n          body.stream(),\n          client,\n          request,\n          client[kSocket],\n          contentLength,\n          expectsPayload\n        )\n      } else {\n        writeBlob(\n          abort,\n          stream,\n          body,\n          client,\n          request,\n          client[kSocket],\n          contentLength,\n          expectsPayload\n        )\n      }\n    } else if (util.isStream(body)) {\n      writeStream(\n        abort,\n        client[kSocket],\n        expectsPayload,\n        stream,\n        body,\n        client,\n        request,\n        contentLength\n      )\n    } else if (util.isIterable(body)) {\n      writeIterable(\n        abort,\n        stream,\n        body,\n        client,\n        request,\n        client[kSocket],\n        contentLength,\n        expectsPayload\n      )\n    } else {\n      assert(false)\n    }\n  }\n}\n\nfunction writeBuffer (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {\n  try {\n    if (body != null && util.isBuffer(body)) {\n      assert(contentLength === body.byteLength, 'buffer body must have content length')\n      h2stream.cork()\n      h2stream.write(body)\n      h2stream.uncork()\n      h2stream.end()\n\n      request.onBodySent(body)\n    }\n\n    if (!expectsPayload) {\n      socket[kReset] = true\n    }\n\n    request.onRequestSent()\n    client[kResume]()\n  } catch (error) {\n    abort(error)\n  }\n}\n\nfunction writeStream (abort, socket, expectsPayload, h2stream, body, client, request, contentLength) {\n  assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined')\n\n  // For HTTP/2, is enough to pipe the stream\n  const pipe = pipeline(\n    body,\n    h2stream,\n    (err) => {\n      if (err) {\n        util.destroy(pipe, err)\n        abort(err)\n      } else {\n        util.removeAllListeners(pipe)\n        request.onRequestSent()\n\n        if (!expectsPayload) {\n          socket[kReset] = true\n        }\n\n        client[kResume]()\n      }\n    }\n  )\n\n  util.addListener(pipe, 'data', onPipeData)\n\n  function onPipeData (chunk) {\n    request.onBodySent(chunk)\n  }\n}\n\nasync function writeBlob (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {\n  assert(contentLength === body.size, 'blob body must have content length')\n\n  try {\n    if (contentLength != null && contentLength !== body.size) {\n      throw new RequestContentLengthMismatchError()\n    }\n\n    const buffer = Buffer.from(await body.arrayBuffer())\n\n    h2stream.cork()\n    h2stream.write(buffer)\n    h2stream.uncork()\n    h2stream.end()\n\n    request.onBodySent(buffer)\n    request.onRequestSent()\n\n    if (!expectsPayload) {\n      socket[kReset] = true\n    }\n\n    client[kResume]()\n  } catch (err) {\n    abort(err)\n  }\n}\n\nasync function writeIterable (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {\n  assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined')\n\n  let callback = null\n  function onDrain () {\n    if (callback) {\n      const cb = callback\n      callback = null\n      cb()\n    }\n  }\n\n  const waitForDrain = () => new Promise((resolve, reject) => {\n    assert(callback === null)\n\n    if (socket[kError]) {\n      reject(socket[kError])\n    } else {\n      callback = resolve\n    }\n  })\n\n  h2stream\n    .on('close', onDrain)\n    .on('drain', onDrain)\n\n  try {\n    // It's up to the user to somehow abort the async iterable.\n    for await (const chunk of body) {\n      if (socket[kError]) {\n        throw socket[kError]\n      }\n\n      const res = h2stream.write(chunk)\n      request.onBodySent(chunk)\n      if (!res) {\n        await waitForDrain()\n      }\n    }\n\n    h2stream.end()\n\n    request.onRequestSent()\n\n    if (!expectsPayload) {\n      socket[kReset] = true\n    }\n\n    client[kResume]()\n  } catch (err) {\n    abort(err)\n  } finally {\n    h2stream\n      .off('close', onDrain)\n      .off('drain', onDrain)\n  }\n}\n\nmodule.exports = connectH2\n"
  },
  {
    "path": "lib/dispatcher/client.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst net = require('node:net')\nconst http = require('node:http')\nconst util = require('../core/util.js')\nconst { ClientStats } = require('../util/stats.js')\nconst { channels } = require('../core/diagnostics.js')\nconst Request = require('../core/request.js')\nconst DispatcherBase = require('./dispatcher-base')\nconst {\n  InvalidArgumentError,\n  InformationalError,\n  ClientDestroyedError\n} = require('../core/errors.js')\nconst buildConnector = require('../core/connect.js')\nconst {\n  kUrl,\n  kServerName,\n  kClient,\n  kBusy,\n  kConnect,\n  kResuming,\n  kRunning,\n  kPending,\n  kSize,\n  kQueue,\n  kConnected,\n  kConnecting,\n  kNeedDrain,\n  kKeepAliveDefaultTimeout,\n  kHostHeader,\n  kPendingIdx,\n  kRunningIdx,\n  kError,\n  kPipelining,\n  kKeepAliveTimeoutValue,\n  kMaxHeadersSize,\n  kKeepAliveMaxTimeout,\n  kKeepAliveTimeoutThreshold,\n  kHeadersTimeout,\n  kBodyTimeout,\n  kStrictContentLength,\n  kConnector,\n  kMaxRequests,\n  kCounter,\n  kClose,\n  kDestroy,\n  kDispatch,\n  kLocalAddress,\n  kMaxResponseSize,\n  kOnError,\n  kHTTPContext,\n  kMaxConcurrentStreams,\n  kHTTP2InitialWindowSize,\n  kHTTP2ConnectionWindowSize,\n  kResume,\n  kPingInterval\n} = require('../core/symbols.js')\nconst connectH1 = require('./client-h1.js')\nconst connectH2 = require('./client-h2.js')\n\nconst kClosedResolve = Symbol('kClosedResolve')\n\nconst getDefaultNodeMaxHeaderSize = http &&\n  http.maxHeaderSize &&\n  Number.isInteger(http.maxHeaderSize) &&\n  http.maxHeaderSize > 0\n  ? () => http.maxHeaderSize\n  : () => { throw new InvalidArgumentError('http module not available or http.maxHeaderSize invalid') }\n\nconst noop = () => { }\n\nfunction getPipelining (client) {\n  return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1\n}\n\n/**\n * @type {import('../../types/client.js').default}\n */\nclass Client extends DispatcherBase {\n  /**\n   *\n   * @param {string|URL} url\n   * @param {import('../../types/client.js').Client.Options} options\n   */\n  constructor (url, {\n    maxHeaderSize,\n    headersTimeout,\n    socketTimeout,\n    requestTimeout,\n    connectTimeout,\n    bodyTimeout,\n    idleTimeout,\n    keepAlive,\n    keepAliveTimeout,\n    maxKeepAliveTimeout,\n    keepAliveMaxTimeout,\n    keepAliveTimeoutThreshold,\n    socketPath,\n    pipelining,\n    tls,\n    strictContentLength,\n    maxCachedSessions,\n    connect,\n    maxRequestsPerClient,\n    localAddress,\n    maxResponseSize,\n    autoSelectFamily,\n    autoSelectFamilyAttemptTimeout,\n    // h2\n    maxConcurrentStreams,\n    allowH2,\n    useH2c,\n    initialWindowSize,\n    connectionWindowSize,\n    pingInterval\n  } = {}) {\n    if (keepAlive !== undefined) {\n      throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')\n    }\n\n    if (socketTimeout !== undefined) {\n      throw new InvalidArgumentError('unsupported socketTimeout, use headersTimeout & bodyTimeout instead')\n    }\n\n    if (requestTimeout !== undefined) {\n      throw new InvalidArgumentError('unsupported requestTimeout, use headersTimeout & bodyTimeout instead')\n    }\n\n    if (idleTimeout !== undefined) {\n      throw new InvalidArgumentError('unsupported idleTimeout, use keepAliveTimeout instead')\n    }\n\n    if (maxKeepAliveTimeout !== undefined) {\n      throw new InvalidArgumentError('unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead')\n    }\n\n    if (maxHeaderSize != null) {\n      if (!Number.isInteger(maxHeaderSize) || maxHeaderSize < 1) {\n        throw new InvalidArgumentError('invalid maxHeaderSize')\n      }\n    } else {\n      // If maxHeaderSize is not provided, use the default value from the http module\n      // or if that is not available, throw an error.\n      maxHeaderSize = getDefaultNodeMaxHeaderSize()\n    }\n\n    if (socketPath != null && typeof socketPath !== 'string') {\n      throw new InvalidArgumentError('invalid socketPath')\n    }\n\n    if (connectTimeout != null && (!Number.isFinite(connectTimeout) || connectTimeout < 0)) {\n      throw new InvalidArgumentError('invalid connectTimeout')\n    }\n\n    if (keepAliveTimeout != null && (!Number.isFinite(keepAliveTimeout) || keepAliveTimeout <= 0)) {\n      throw new InvalidArgumentError('invalid keepAliveTimeout')\n    }\n\n    if (keepAliveMaxTimeout != null && (!Number.isFinite(keepAliveMaxTimeout) || keepAliveMaxTimeout <= 0)) {\n      throw new InvalidArgumentError('invalid keepAliveMaxTimeout')\n    }\n\n    if (keepAliveTimeoutThreshold != null && !Number.isFinite(keepAliveTimeoutThreshold)) {\n      throw new InvalidArgumentError('invalid keepAliveTimeoutThreshold')\n    }\n\n    if (headersTimeout != null && (!Number.isInteger(headersTimeout) || headersTimeout < 0)) {\n      throw new InvalidArgumentError('headersTimeout must be a positive integer or zero')\n    }\n\n    if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) {\n      throw new InvalidArgumentError('bodyTimeout must be a positive integer or zero')\n    }\n\n    if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {\n      throw new InvalidArgumentError('connect must be a function or an object')\n    }\n\n    if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {\n      throw new InvalidArgumentError('maxRequestsPerClient must be a positive number')\n    }\n\n    if (localAddress != null && (typeof localAddress !== 'string' || net.isIP(localAddress) === 0)) {\n      throw new InvalidArgumentError('localAddress must be valid string IP address')\n    }\n\n    if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) {\n      throw new InvalidArgumentError('maxResponseSize must be a positive number')\n    }\n\n    if (\n      autoSelectFamilyAttemptTimeout != null &&\n      (!Number.isInteger(autoSelectFamilyAttemptTimeout) || autoSelectFamilyAttemptTimeout < -1)\n    ) {\n      throw new InvalidArgumentError('autoSelectFamilyAttemptTimeout must be a positive number')\n    }\n\n    // h2\n    if (allowH2 != null && typeof allowH2 !== 'boolean') {\n      throw new InvalidArgumentError('allowH2 must be a valid boolean value')\n    }\n\n    if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1)) {\n      throw new InvalidArgumentError('maxConcurrentStreams must be a positive integer, greater than 0')\n    }\n\n    if (useH2c != null && typeof useH2c !== 'boolean') {\n      throw new InvalidArgumentError('useH2c must be a valid boolean value')\n    }\n\n    if (initialWindowSize != null && (!Number.isInteger(initialWindowSize) || initialWindowSize < 1)) {\n      throw new InvalidArgumentError('initialWindowSize must be a positive integer, greater than 0')\n    }\n\n    if (connectionWindowSize != null && (!Number.isInteger(connectionWindowSize) || connectionWindowSize < 1)) {\n      throw new InvalidArgumentError('connectionWindowSize must be a positive integer, greater than 0')\n    }\n\n    if (pingInterval != null && (typeof pingInterval !== 'number' || !Number.isInteger(pingInterval) || pingInterval < 0)) {\n      throw new InvalidArgumentError('pingInterval must be a positive integer, greater or equal to 0')\n    }\n\n    super()\n\n    if (typeof connect !== 'function') {\n      connect = buildConnector({\n        ...tls,\n        maxCachedSessions,\n        allowH2,\n        useH2c,\n        socketPath,\n        timeout: connectTimeout,\n        ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),\n        ...connect\n      })\n    } else if (socketPath != null) {\n      const customConnect = connect\n      connect = (opts, callback) => customConnect({ ...opts, socketPath }, callback)\n    }\n\n    this[kUrl] = util.parseOrigin(url)\n    this[kConnector] = connect\n    this[kPipelining] = pipelining != null ? pipelining : 1\n    this[kMaxHeadersSize] = maxHeaderSize\n    this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout\n    this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout\n    this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 2e3 : keepAliveTimeoutThreshold\n    this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout]\n    this[kServerName] = null\n    this[kLocalAddress] = localAddress != null ? localAddress : null\n    this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming\n    this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming\n    this[kHostHeader] = `host: ${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}\\r\\n`\n    this[kBodyTimeout] = bodyTimeout != null ? bodyTimeout : 300e3\n    this[kHeadersTimeout] = headersTimeout != null ? headersTimeout : 300e3\n    this[kStrictContentLength] = strictContentLength == null ? true : strictContentLength\n    this[kMaxRequests] = maxRequestsPerClient\n    this[kClosedResolve] = null\n    this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1\n    this[kHTTPContext] = null\n    // h2\n    this[kMaxConcurrentStreams] = maxConcurrentStreams != null ? maxConcurrentStreams : 100 // Max peerConcurrentStreams for a Node h2 server\n    // HTTP/2 window sizes are set to higher defaults than Node.js core for better performance:\n    // - initialWindowSize: 262144 (256KB) vs Node.js default 65535 (64KB - 1)\n    //   Allows more data to be sent before requiring acknowledgment, improving throughput\n    //   especially on high-latency networks. This matches common production HTTP/2 servers.\n    // - connectionWindowSize: 524288 (512KB) vs Node.js default (none set)\n    //   Provides better flow control for the entire connection across multiple streams.\n    this[kHTTP2InitialWindowSize] = initialWindowSize != null ? initialWindowSize : 262144\n    this[kHTTP2ConnectionWindowSize] = connectionWindowSize != null ? connectionWindowSize : 524288\n    this[kPingInterval] = pingInterval != null ? pingInterval : 60e3 // Default ping interval for h2 - 1 minute\n\n    // kQueue is built up of 3 sections separated by\n    // the kRunningIdx and kPendingIdx indices.\n    // |   complete   |   running   |   pending   |\n    //                ^ kRunningIdx ^ kPendingIdx ^ kQueue.length\n    // kRunningIdx points to the first running element.\n    // kPendingIdx points to the first pending element.\n    // This implements a fast queue with an amortized\n    // time of O(1).\n\n    this[kQueue] = []\n    this[kRunningIdx] = 0\n    this[kPendingIdx] = 0\n\n    this[kResume] = (sync) => resume(this, sync)\n    this[kOnError] = (err) => onError(this, err)\n  }\n\n  get pipelining () {\n    return this[kPipelining]\n  }\n\n  set pipelining (value) {\n    this[kPipelining] = value\n    this[kResume](true)\n  }\n\n  get stats () {\n    return new ClientStats(this)\n  }\n\n  get [kPending] () {\n    return this[kQueue].length - this[kPendingIdx]\n  }\n\n  get [kRunning] () {\n    return this[kPendingIdx] - this[kRunningIdx]\n  }\n\n  get [kSize] () {\n    return this[kQueue].length - this[kRunningIdx]\n  }\n\n  get [kConnected] () {\n    return !!this[kHTTPContext] && !this[kConnecting] && !this[kHTTPContext].destroyed\n  }\n\n  get [kBusy] () {\n    return Boolean(\n      this[kHTTPContext]?.busy(null) ||\n      (this[kSize] >= (getPipelining(this) || 1)) ||\n      this[kPending] > 0\n    )\n  }\n\n  [kConnect] (cb) {\n    connect(this)\n    this.once('connect', cb)\n  }\n\n  [kDispatch] (opts, handler) {\n    const request = new Request(this[kUrl].origin, opts, handler)\n\n    this[kQueue].push(request)\n    if (this[kResuming]) {\n      // Do nothing.\n    } else if (util.bodyLength(request.body) == null && util.isIterable(request.body)) {\n      // Wait a tick in case stream/iterator is ended in the same tick.\n      this[kResuming] = 1\n      queueMicrotask(() => resume(this))\n    } else {\n      this[kResume](true)\n    }\n\n    if (this[kResuming] && this[kNeedDrain] !== 2 && this[kBusy]) {\n      this[kNeedDrain] = 2\n    }\n\n    return this[kNeedDrain] < 2\n  }\n\n  [kClose] () {\n    // TODO: for H2 we need to gracefully flush the remaining enqueued\n    // request and close each stream.\n    return new Promise((resolve) => {\n      if (this[kSize]) {\n        this[kClosedResolve] = resolve\n      } else {\n        resolve(null)\n      }\n    })\n  }\n\n  [kDestroy] (err) {\n    return new Promise((resolve) => {\n      const requests = this[kQueue].splice(this[kPendingIdx])\n      for (let i = 0; i < requests.length; i++) {\n        const request = requests[i]\n        util.errorRequest(this, request, err)\n      }\n\n      const callback = () => {\n        if (this[kClosedResolve]) {\n          // TODO (fix): Should we error here with ClientDestroyedError?\n          this[kClosedResolve]()\n          this[kClosedResolve] = null\n        }\n        resolve(null)\n      }\n\n      if (this[kHTTPContext]) {\n        this[kHTTPContext].destroy(err, callback)\n        this[kHTTPContext] = null\n      } else {\n        queueMicrotask(callback)\n      }\n\n      this[kResume]()\n    })\n  }\n}\n\nfunction onError (client, err) {\n  if (\n    client[kRunning] === 0 &&\n    err.code !== 'UND_ERR_INFO' &&\n    err.code !== 'UND_ERR_SOCKET'\n  ) {\n    // Error is not caused by running request and not a recoverable\n    // socket error.\n\n    assert(client[kPendingIdx] === client[kRunningIdx])\n\n    const requests = client[kQueue].splice(client[kRunningIdx])\n\n    for (let i = 0; i < requests.length; i++) {\n      const request = requests[i]\n      util.errorRequest(client, request, err)\n    }\n    assert(client[kSize] === 0)\n  }\n}\n\n/**\n * @param {Client} client\n * @returns {void}\n */\nfunction connect (client) {\n  assert(!client[kConnecting])\n  assert(!client[kHTTPContext])\n\n  let { host, hostname, protocol, port } = client[kUrl]\n\n  // Resolve ipv6\n  if (hostname[0] === '[') {\n    const idx = hostname.indexOf(']')\n\n    assert(idx !== -1)\n    const ip = hostname.substring(1, idx)\n\n    assert(net.isIPv6(ip))\n    hostname = ip\n  }\n\n  client[kConnecting] = true\n\n  if (channels.beforeConnect.hasSubscribers) {\n    channels.beforeConnect.publish({\n      connectParams: {\n        host,\n        hostname,\n        protocol,\n        port,\n        version: client[kHTTPContext]?.version,\n        servername: client[kServerName],\n        localAddress: client[kLocalAddress]\n      },\n      connector: client[kConnector]\n    })\n  }\n\n  client[kConnector]({\n    host,\n    hostname,\n    protocol,\n    port,\n    servername: client[kServerName],\n    localAddress: client[kLocalAddress]\n  }, (err, socket) => {\n    if (err) {\n      handleConnectError(client, err, { host, hostname, protocol, port })\n      client[kResume]()\n      return\n    }\n\n    if (client.destroyed) {\n      util.destroy(socket.on('error', noop), new ClientDestroyedError())\n      client[kResume]()\n      return\n    }\n\n    assert(socket)\n\n    try {\n      client[kHTTPContext] = socket.alpnProtocol === 'h2'\n        ? connectH2(client, socket)\n        : connectH1(client, socket)\n    } catch (err) {\n      socket.destroy().on('error', noop)\n      handleConnectError(client, err, { host, hostname, protocol, port })\n      client[kResume]()\n      return\n    }\n\n    client[kConnecting] = false\n\n    socket[kCounter] = 0\n    socket[kMaxRequests] = client[kMaxRequests]\n    socket[kClient] = client\n    socket[kError] = null\n\n    if (channels.connected.hasSubscribers) {\n      channels.connected.publish({\n        connectParams: {\n          host,\n          hostname,\n          protocol,\n          port,\n          version: client[kHTTPContext]?.version,\n          servername: client[kServerName],\n          localAddress: client[kLocalAddress]\n        },\n        connector: client[kConnector],\n        socket\n      })\n    }\n\n    client.emit('connect', client[kUrl], [client])\n    client[kResume]()\n  })\n}\n\nfunction handleConnectError (client, err, { host, hostname, protocol, port }) {\n  if (client.destroyed) {\n    return\n  }\n\n  client[kConnecting] = false\n\n  if (channels.connectError.hasSubscribers) {\n    channels.connectError.publish({\n      connectParams: {\n        host,\n        hostname,\n        protocol,\n        port,\n        version: client[kHTTPContext]?.version,\n        servername: client[kServerName],\n        localAddress: client[kLocalAddress]\n      },\n      connector: client[kConnector],\n      error: err\n    })\n  }\n\n  if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {\n    assert(client[kRunning] === 0)\n    while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) {\n      const request = client[kQueue][client[kPendingIdx]++]\n      util.errorRequest(client, request, err)\n    }\n  } else {\n    onError(client, err)\n  }\n\n  client.emit('connectionError', client[kUrl], [client], err)\n}\n\nfunction emitDrain (client) {\n  client[kNeedDrain] = 0\n  client.emit('drain', client[kUrl], [client])\n}\n\nfunction resume (client, sync) {\n  if (client[kResuming] === 2) {\n    return\n  }\n\n  client[kResuming] = 2\n\n  _resume(client, sync)\n  client[kResuming] = 0\n\n  if (client[kRunningIdx] > 256) {\n    client[kQueue].splice(0, client[kRunningIdx])\n    client[kPendingIdx] -= client[kRunningIdx]\n    client[kRunningIdx] = 0\n  }\n}\n\nfunction _resume (client, sync) {\n  while (true) {\n    if (client.destroyed) {\n      assert(client[kPending] === 0)\n      return\n    }\n\n    if (client[kClosedResolve] && !client[kSize]) {\n      client[kClosedResolve]()\n      client[kClosedResolve] = null\n      return\n    }\n\n    if (client[kHTTPContext]) {\n      client[kHTTPContext].resume()\n    }\n\n    if (client[kBusy]) {\n      client[kNeedDrain] = 2\n    } else if (client[kNeedDrain] === 2) {\n      if (sync) {\n        client[kNeedDrain] = 1\n        queueMicrotask(() => emitDrain(client))\n      } else {\n        emitDrain(client)\n      }\n      continue\n    }\n\n    if (client[kPending] === 0) {\n      return\n    }\n\n    if (client[kRunning] >= (getPipelining(client) || 1)) {\n      return\n    }\n\n    const request = client[kQueue][client[kPendingIdx]]\n\n    if (request === null) {\n      return\n    }\n\n    if (client[kUrl].protocol === 'https:' && client[kServerName] !== request.servername) {\n      if (client[kRunning] > 0) {\n        return\n      }\n\n      client[kServerName] = request.servername\n      client[kHTTPContext]?.destroy(new InformationalError('servername changed'), () => {\n        client[kHTTPContext] = null\n        resume(client)\n      })\n    }\n\n    if (client[kConnecting]) {\n      return\n    }\n\n    if (!client[kHTTPContext]) {\n      connect(client)\n      return\n    }\n\n    if (client[kHTTPContext].destroyed) {\n      return\n    }\n\n    if (client[kHTTPContext].busy(request)) {\n      return\n    }\n\n    if (!request.aborted && client[kHTTPContext].write(request)) {\n      client[kPendingIdx]++\n    } else {\n      client[kQueue].splice(client[kPendingIdx], 1)\n    }\n  }\n}\n\nmodule.exports = Client\n"
  },
  {
    "path": "lib/dispatcher/dispatcher-base.js",
    "content": "'use strict'\n\nconst Dispatcher = require('./dispatcher')\nconst UnwrapHandler = require('../handler/unwrap-handler')\nconst {\n  ClientDestroyedError,\n  ClientClosedError,\n  InvalidArgumentError\n} = require('../core/errors')\nconst { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/symbols')\n\nconst kOnDestroyed = Symbol('onDestroyed')\nconst kOnClosed = Symbol('onClosed')\n\nclass DispatcherBase extends Dispatcher {\n  /** @type {boolean} */\n  [kDestroyed] = false;\n\n  /** @type {Array<Function|null} */\n  [kOnDestroyed] = null;\n\n  /** @type {boolean} */\n  [kClosed] = false;\n\n  /** @type {Array<Function>|null} */\n  [kOnClosed] = null\n\n  /** @returns {boolean} */\n  get destroyed () {\n    return this[kDestroyed]\n  }\n\n  /** @returns {boolean} */\n  get closed () {\n    return this[kClosed]\n  }\n\n  close (callback) {\n    if (callback === undefined) {\n      return new Promise((resolve, reject) => {\n        this.close((err, data) => {\n          return err ? reject(err) : resolve(data)\n        })\n      })\n    }\n\n    if (typeof callback !== 'function') {\n      throw new InvalidArgumentError('invalid callback')\n    }\n\n    if (this[kDestroyed]) {\n      const err = new ClientDestroyedError()\n      queueMicrotask(() => callback(err, null))\n      return\n    }\n\n    if (this[kClosed]) {\n      if (this[kOnClosed]) {\n        this[kOnClosed].push(callback)\n      } else {\n        queueMicrotask(() => callback(null, null))\n      }\n      return\n    }\n\n    this[kClosed] = true\n    this[kOnClosed] ??= []\n    this[kOnClosed].push(callback)\n\n    const onClosed = () => {\n      const callbacks = this[kOnClosed]\n      this[kOnClosed] = null\n      for (let i = 0; i < callbacks.length; i++) {\n        callbacks[i](null, null)\n      }\n    }\n\n    // Should not error.\n    this[kClose]()\n      .then(() => this.destroy())\n      .then(() => queueMicrotask(onClosed))\n  }\n\n  destroy (err, callback) {\n    if (typeof err === 'function') {\n      callback = err\n      err = null\n    }\n\n    if (callback === undefined) {\n      return new Promise((resolve, reject) => {\n        this.destroy(err, (err, data) => {\n          return err ? reject(err) : resolve(data)\n        })\n      })\n    }\n\n    if (typeof callback !== 'function') {\n      throw new InvalidArgumentError('invalid callback')\n    }\n\n    if (this[kDestroyed]) {\n      if (this[kOnDestroyed]) {\n        this[kOnDestroyed].push(callback)\n      } else {\n        queueMicrotask(() => callback(null, null))\n      }\n      return\n    }\n\n    if (!err) {\n      err = new ClientDestroyedError()\n    }\n\n    this[kDestroyed] = true\n    this[kOnDestroyed] ??= []\n    this[kOnDestroyed].push(callback)\n\n    const onDestroyed = () => {\n      const callbacks = this[kOnDestroyed]\n      this[kOnDestroyed] = null\n      for (let i = 0; i < callbacks.length; i++) {\n        callbacks[i](null, null)\n      }\n    }\n\n    // Should not error.\n    this[kDestroy](err)\n      .then(() => queueMicrotask(onDestroyed))\n  }\n\n  dispatch (opts, handler) {\n    if (!handler || typeof handler !== 'object') {\n      throw new InvalidArgumentError('handler must be an object')\n    }\n\n    handler = UnwrapHandler.unwrap(handler)\n\n    try {\n      if (!opts || typeof opts !== 'object') {\n        throw new InvalidArgumentError('opts must be an object.')\n      }\n\n      if (this[kDestroyed] || this[kOnDestroyed]) {\n        throw new ClientDestroyedError()\n      }\n\n      if (this[kClosed]) {\n        throw new ClientClosedError()\n      }\n\n      return this[kDispatch](opts, handler)\n    } catch (err) {\n      if (typeof handler.onError !== 'function') {\n        throw err\n      }\n\n      handler.onError(err)\n\n      return false\n    }\n  }\n}\n\nmodule.exports = DispatcherBase\n"
  },
  {
    "path": "lib/dispatcher/dispatcher.js",
    "content": "'use strict'\nconst EventEmitter = require('node:events')\nconst WrapHandler = require('../handler/wrap-handler')\n\nconst wrapInterceptor = (dispatch) => (opts, handler) => dispatch(opts, WrapHandler.wrap(handler))\n\nclass Dispatcher extends EventEmitter {\n  dispatch () {\n    throw new Error('not implemented')\n  }\n\n  close () {\n    throw new Error('not implemented')\n  }\n\n  destroy () {\n    throw new Error('not implemented')\n  }\n\n  compose (...args) {\n    // So we handle [interceptor1, interceptor2] or interceptor1, interceptor2, ...\n    const interceptors = Array.isArray(args[0]) ? args[0] : args\n    let dispatch = this.dispatch.bind(this)\n\n    for (const interceptor of interceptors) {\n      if (interceptor == null) {\n        continue\n      }\n\n      if (typeof interceptor !== 'function') {\n        throw new TypeError(`invalid interceptor, expected function received ${typeof interceptor}`)\n      }\n\n      dispatch = interceptor(dispatch)\n      dispatch = wrapInterceptor(dispatch)\n\n      if (dispatch == null || typeof dispatch !== 'function' || dispatch.length !== 2) {\n        throw new TypeError('invalid interceptor')\n      }\n    }\n\n    return new Proxy(this, {\n      get: (target, key) => key === 'dispatch' ? dispatch : target[key]\n    })\n  }\n}\n\nmodule.exports = Dispatcher\n"
  },
  {
    "path": "lib/dispatcher/env-http-proxy-agent.js",
    "content": "'use strict'\n\nconst DispatcherBase = require('./dispatcher-base')\nconst { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = require('../core/symbols')\nconst ProxyAgent = require('./proxy-agent')\nconst Agent = require('./agent')\n\nconst DEFAULT_PORTS = {\n  'http:': 80,\n  'https:': 443\n}\n\nclass EnvHttpProxyAgent extends DispatcherBase {\n  #noProxyValue = null\n  #noProxyEntries = null\n  #opts = null\n\n  constructor (opts = {}) {\n    super()\n    this.#opts = opts\n\n    const { httpProxy, httpsProxy, noProxy, ...agentOpts } = opts\n\n    this[kNoProxyAgent] = new Agent(agentOpts)\n\n    const HTTP_PROXY = httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY\n    if (HTTP_PROXY) {\n      this[kHttpProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTP_PROXY })\n    } else {\n      this[kHttpProxyAgent] = this[kNoProxyAgent]\n    }\n\n    const HTTPS_PROXY = httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY\n    if (HTTPS_PROXY) {\n      this[kHttpsProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTPS_PROXY })\n    } else {\n      this[kHttpsProxyAgent] = this[kHttpProxyAgent]\n    }\n\n    this.#parseNoProxy()\n  }\n\n  [kDispatch] (opts, handler) {\n    const url = new URL(opts.origin)\n    const agent = this.#getProxyAgentForUrl(url)\n    return agent.dispatch(opts, handler)\n  }\n\n  [kClose] () {\n    return Promise.all([\n      this[kNoProxyAgent].close(),\n      !this[kHttpProxyAgent][kClosed] && this[kHttpProxyAgent].close(),\n      !this[kHttpsProxyAgent][kClosed] && this[kHttpsProxyAgent].close()\n    ])\n  }\n\n  [kDestroy] (err) {\n    return Promise.all([\n      this[kNoProxyAgent].destroy(err),\n      !this[kHttpProxyAgent][kDestroyed] && this[kHttpProxyAgent].destroy(err),\n      !this[kHttpsProxyAgent][kDestroyed] && this[kHttpsProxyAgent].destroy(err)\n    ])\n  }\n\n  #getProxyAgentForUrl (url) {\n    let { protocol, host: hostname, port } = url\n\n    // Stripping ports in this way instead of using parsedUrl.hostname to make\n    // sure that the brackets around IPv6 addresses are kept.\n    hostname = hostname.replace(/:\\d*$/, '').toLowerCase()\n    port = Number.parseInt(port, 10) || DEFAULT_PORTS[protocol] || 0\n    if (!this.#shouldProxy(hostname, port)) {\n      return this[kNoProxyAgent]\n    }\n    if (protocol === 'https:') {\n      return this[kHttpsProxyAgent]\n    }\n    return this[kHttpProxyAgent]\n  }\n\n  #shouldProxy (hostname, port) {\n    if (this.#noProxyChanged) {\n      this.#parseNoProxy()\n    }\n\n    if (this.#noProxyEntries.length === 0) {\n      return true // Always proxy if NO_PROXY is not set or empty.\n    }\n    if (this.#noProxyValue === '*') {\n      return false // Never proxy if wildcard is set.\n    }\n\n    for (let i = 0; i < this.#noProxyEntries.length; i++) {\n      const entry = this.#noProxyEntries[i]\n      if (entry.port && entry.port !== port) {\n        continue // Skip if ports don't match.\n      }\n      // Don't proxy if the hostname is equal with the no_proxy host.\n      if (hostname === entry.hostname) {\n        return false\n      }\n      // Don't proxy if the hostname is the subdomain of the no_proxy host.\n      // Reference - https://github.com/denoland/deno/blob/6fbce91e40cc07fc6da74068e5cc56fdd40f7b4c/ext/fetch/proxy.rs#L485\n      if (hostname.slice(-(entry.hostname.length + 1)) === `.${entry.hostname}`) {\n        return false\n      }\n    }\n\n    return true\n  }\n\n  #parseNoProxy () {\n    const noProxyValue = this.#opts.noProxy ?? this.#noProxyEnv\n    const noProxySplit = noProxyValue.split(/[,\\s]/)\n    const noProxyEntries = []\n\n    for (let i = 0; i < noProxySplit.length; i++) {\n      const entry = noProxySplit[i]\n      if (!entry) {\n        continue\n      }\n      const parsed = entry.match(/^(.+):(\\d+)$/)\n      noProxyEntries.push({\n        // strip leading dot or asterisk with dot\n        hostname: (parsed ? parsed[1] : entry).replace(/^\\*?\\./, '').toLowerCase(),\n        port: parsed ? Number.parseInt(parsed[2], 10) : 0\n      })\n    }\n\n    this.#noProxyValue = noProxyValue\n    this.#noProxyEntries = noProxyEntries\n  }\n\n  get #noProxyChanged () {\n    if (this.#opts.noProxy !== undefined) {\n      return false\n    }\n    return this.#noProxyValue !== this.#noProxyEnv\n  }\n\n  get #noProxyEnv () {\n    return process.env.no_proxy ?? process.env.NO_PROXY ?? ''\n  }\n}\n\nmodule.exports = EnvHttpProxyAgent\n"
  },
  {
    "path": "lib/dispatcher/fixed-queue.js",
    "content": "'use strict'\n\n// Extracted from node/lib/internal/fixed_queue.js\n\n// Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two.\nconst kSize = 2048\nconst kMask = kSize - 1\n\n// The FixedQueue is implemented as a singly-linked list of fixed-size\n// circular buffers. It looks something like this:\n//\n//  head                                                       tail\n//    |                                                          |\n//    v                                                          v\n// +-----------+ <-----\\       +-----------+ <------\\         +-----------+\n// |  [null]   |        \\----- |   next    |         \\------- |   next    |\n// +-----------+               +-----------+                  +-----------+\n// |   item    | <-- bottom    |   item    | <-- bottom       | undefined |\n// |   item    |               |   item    |                  | undefined |\n// |   item    |               |   item    |                  | undefined |\n// |   item    |               |   item    |                  | undefined |\n// |   item    |               |   item    |       bottom --> |   item    |\n// |   item    |               |   item    |                  |   item    |\n// |    ...    |               |    ...    |                  |    ...    |\n// |   item    |               |   item    |                  |   item    |\n// |   item    |               |   item    |                  |   item    |\n// | undefined | <-- top       |   item    |                  |   item    |\n// | undefined |               |   item    |                  |   item    |\n// | undefined |               | undefined | <-- top  top --> | undefined |\n// +-----------+               +-----------+                  +-----------+\n//\n// Or, if there is only one circular buffer, it looks something\n// like either of these:\n//\n//  head   tail                                 head   tail\n//    |     |                                     |     |\n//    v     v                                     v     v\n// +-----------+                               +-----------+\n// |  [null]   |                               |  [null]   |\n// +-----------+                               +-----------+\n// | undefined |                               |   item    |\n// | undefined |                               |   item    |\n// |   item    | <-- bottom            top --> | undefined |\n// |   item    |                               | undefined |\n// | undefined | <-- top            bottom --> |   item    |\n// | undefined |                               |   item    |\n// +-----------+                               +-----------+\n//\n// Adding a value means moving `top` forward by one, removing means\n// moving `bottom` forward by one. After reaching the end, the queue\n// wraps around.\n//\n// When `top === bottom` the current queue is empty and when\n// `top + 1 === bottom` it's full. This wastes a single space of storage\n// but allows much quicker checks.\n\n/**\n * @type {FixedCircularBuffer}\n * @template T\n */\nclass FixedCircularBuffer {\n  /** @type {number} */\n  bottom = 0\n  /** @type {number} */\n  top = 0\n  /** @type {Array<T|undefined>} */\n  list = new Array(kSize).fill(undefined)\n  /** @type {T|null} */\n  next = null\n\n  /** @returns {boolean} */\n  isEmpty () {\n    return this.top === this.bottom\n  }\n\n  /** @returns {boolean} */\n  isFull () {\n    return ((this.top + 1) & kMask) === this.bottom\n  }\n\n  /**\n   * @param {T} data\n   * @returns {void}\n   */\n  push (data) {\n    this.list[this.top] = data\n    this.top = (this.top + 1) & kMask\n  }\n\n  /** @returns {T|null} */\n  shift () {\n    const nextItem = this.list[this.bottom]\n    if (nextItem === undefined) { return null }\n    this.list[this.bottom] = undefined\n    this.bottom = (this.bottom + 1) & kMask\n    return nextItem\n  }\n}\n\n/**\n * @template T\n */\nmodule.exports = class FixedQueue {\n  constructor () {\n    /** @type {FixedCircularBuffer<T>} */\n    this.head = this.tail = new FixedCircularBuffer()\n  }\n\n  /** @returns {boolean} */\n  isEmpty () {\n    return this.head.isEmpty()\n  }\n\n  /** @param {T} data */\n  push (data) {\n    if (this.head.isFull()) {\n      // Head is full: Creates a new queue, sets the old queue's `.next` to it,\n      // and sets it as the new main queue.\n      this.head = this.head.next = new FixedCircularBuffer()\n    }\n    this.head.push(data)\n  }\n\n  /** @returns {T|null} */\n  shift () {\n    const tail = this.tail\n    const next = tail.shift()\n    if (tail.isEmpty() && tail.next !== null) {\n      // If there is another queue, it forms the new tail.\n      this.tail = tail.next\n      tail.next = null\n    }\n    return next\n  }\n}\n"
  },
  {
    "path": "lib/dispatcher/h2c-client.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError } = require('../core/errors')\nconst Client = require('./client')\n\nclass H2CClient extends Client {\n  constructor (origin, clientOpts) {\n    if (typeof origin === 'string') {\n      origin = new URL(origin)\n    }\n\n    if (origin.protocol !== 'http:') {\n      throw new InvalidArgumentError(\n        'h2c-client: Only h2c protocol is supported'\n      )\n    }\n\n    const { connect, maxConcurrentStreams, pipelining, ...opts } =\n            clientOpts ?? {}\n    let defaultMaxConcurrentStreams = 100\n    let defaultPipelining = 100\n\n    if (\n      maxConcurrentStreams != null &&\n            Number.isInteger(maxConcurrentStreams) &&\n            maxConcurrentStreams > 0\n    ) {\n      defaultMaxConcurrentStreams = maxConcurrentStreams\n    }\n\n    if (pipelining != null && Number.isInteger(pipelining) && pipelining > 0) {\n      defaultPipelining = pipelining\n    }\n\n    if (defaultPipelining > defaultMaxConcurrentStreams) {\n      throw new InvalidArgumentError(\n        'h2c-client: pipelining cannot be greater than maxConcurrentStreams'\n      )\n    }\n\n    super(origin, {\n      ...opts,\n      maxConcurrentStreams: defaultMaxConcurrentStreams,\n      pipelining: defaultPipelining,\n      allowH2: true,\n      useH2c: true\n    })\n  }\n}\n\nmodule.exports = H2CClient\n"
  },
  {
    "path": "lib/dispatcher/pool-base.js",
    "content": "'use strict'\n\nconst { PoolStats } = require('../util/stats.js')\nconst DispatcherBase = require('./dispatcher-base')\nconst FixedQueue = require('./fixed-queue')\nconst { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols')\n\nconst kClients = Symbol('clients')\nconst kNeedDrain = Symbol('needDrain')\nconst kQueue = Symbol('queue')\nconst kClosedResolve = Symbol('closed resolve')\nconst kOnDrain = Symbol('onDrain')\nconst kOnConnect = Symbol('onConnect')\nconst kOnDisconnect = Symbol('onDisconnect')\nconst kOnConnectionError = Symbol('onConnectionError')\nconst kGetDispatcher = Symbol('get dispatcher')\nconst kAddClient = Symbol('add client')\nconst kRemoveClient = Symbol('remove client')\n\nclass PoolBase extends DispatcherBase {\n  [kQueue] = new FixedQueue();\n\n  [kQueued] = 0;\n\n  [kClients] = [];\n\n  [kNeedDrain] = false;\n\n  [kOnDrain] (client, origin, targets) {\n    const queue = this[kQueue]\n\n    let needDrain = false\n\n    while (!needDrain) {\n      const item = queue.shift()\n      if (!item) {\n        break\n      }\n      this[kQueued]--\n      needDrain = !client.dispatch(item.opts, item.handler)\n    }\n\n    client[kNeedDrain] = needDrain\n\n    if (!needDrain && this[kNeedDrain]) {\n      this[kNeedDrain] = false\n      this.emit('drain', origin, [this, ...targets])\n    }\n\n    if (this[kClosedResolve] && queue.isEmpty()) {\n      const closeAll = []\n      for (let i = 0; i < this[kClients].length; i++) {\n        const client = this[kClients][i]\n        if (!client.destroyed) {\n          closeAll.push(client.close())\n        }\n      }\n      return Promise.all(closeAll)\n        .then(this[kClosedResolve])\n    }\n  }\n\n  [kOnConnect] = (origin, targets) => {\n    this.emit('connect', origin, [this, ...targets])\n  };\n\n  [kOnDisconnect] = (origin, targets, err) => {\n    this.emit('disconnect', origin, [this, ...targets], err)\n  };\n\n  [kOnConnectionError] = (origin, targets, err) => {\n    this.emit('connectionError', origin, [this, ...targets], err)\n  }\n\n  get [kBusy] () {\n    return this[kNeedDrain]\n  }\n\n  get [kConnected] () {\n    let ret = 0\n    for (const { [kConnected]: connected } of this[kClients]) {\n      ret += connected\n    }\n    return ret\n  }\n\n  get [kFree] () {\n    let ret = 0\n    for (const { [kConnected]: connected, [kNeedDrain]: needDrain } of this[kClients]) {\n      ret += connected && !needDrain\n    }\n    return ret\n  }\n\n  get [kPending] () {\n    let ret = this[kQueued]\n    for (const { [kPending]: pending } of this[kClients]) {\n      ret += pending\n    }\n    return ret\n  }\n\n  get [kRunning] () {\n    let ret = 0\n    for (const { [kRunning]: running } of this[kClients]) {\n      ret += running\n    }\n    return ret\n  }\n\n  get [kSize] () {\n    let ret = this[kQueued]\n    for (const { [kSize]: size } of this[kClients]) {\n      ret += size\n    }\n    return ret\n  }\n\n  get stats () {\n    return new PoolStats(this)\n  }\n\n  [kClose] () {\n    if (this[kQueue].isEmpty()) {\n      const closeAll = []\n      for (let i = 0; i < this[kClients].length; i++) {\n        const client = this[kClients][i]\n        if (!client.destroyed) {\n          closeAll.push(client.close())\n        }\n      }\n      return Promise.all(closeAll)\n    } else {\n      return new Promise((resolve) => {\n        this[kClosedResolve] = resolve\n      })\n    }\n  }\n\n  [kDestroy] (err) {\n    while (true) {\n      const item = this[kQueue].shift()\n      if (!item) {\n        break\n      }\n      item.handler.onError(err)\n    }\n\n    const destroyAll = new Array(this[kClients].length)\n    for (let i = 0; i < this[kClients].length; i++) {\n      destroyAll[i] = this[kClients][i].destroy(err)\n    }\n    return Promise.all(destroyAll)\n  }\n\n  [kDispatch] (opts, handler) {\n    const dispatcher = this[kGetDispatcher]()\n\n    if (!dispatcher) {\n      this[kNeedDrain] = true\n      this[kQueue].push({ opts, handler })\n      this[kQueued]++\n    } else if (!dispatcher.dispatch(opts, handler)) {\n      dispatcher[kNeedDrain] = true\n      this[kNeedDrain] = !this[kGetDispatcher]()\n    }\n\n    return !this[kNeedDrain]\n  }\n\n  [kAddClient] (client) {\n    client\n      .on('drain', this[kOnDrain].bind(this, client))\n      .on('connect', this[kOnConnect])\n      .on('disconnect', this[kOnDisconnect])\n      .on('connectionError', this[kOnConnectionError])\n\n    this[kClients].push(client)\n\n    if (this[kNeedDrain]) {\n      queueMicrotask(() => {\n        if (this[kNeedDrain]) {\n          this[kOnDrain](client, client[kUrl], [client, this])\n        }\n      })\n    }\n\n    return this\n  }\n\n  [kRemoveClient] (client) {\n    client.close(() => {\n      const idx = this[kClients].indexOf(client)\n      if (idx !== -1) {\n        this[kClients].splice(idx, 1)\n      }\n    })\n\n    this[kNeedDrain] = this[kClients].some(dispatcher => (\n      !dispatcher[kNeedDrain] &&\n      dispatcher.closed !== true &&\n      dispatcher.destroyed !== true\n    ))\n  }\n}\n\nmodule.exports = {\n  PoolBase,\n  kClients,\n  kNeedDrain,\n  kAddClient,\n  kRemoveClient,\n  kGetDispatcher\n}\n"
  },
  {
    "path": "lib/dispatcher/pool.js",
    "content": "'use strict'\n\nconst {\n  PoolBase,\n  kClients,\n  kNeedDrain,\n  kAddClient,\n  kGetDispatcher,\n  kRemoveClient\n} = require('./pool-base')\nconst Client = require('./client')\nconst {\n  InvalidArgumentError\n} = require('../core/errors')\nconst util = require('../core/util')\nconst { kUrl } = require('../core/symbols')\nconst buildConnector = require('../core/connect')\n\nconst kOptions = Symbol('options')\nconst kConnections = Symbol('connections')\nconst kFactory = Symbol('factory')\n\nfunction defaultFactory (origin, opts) {\n  return new Client(origin, opts)\n}\n\nclass Pool extends PoolBase {\n  constructor (origin, {\n    connections,\n    factory = defaultFactory,\n    connect,\n    connectTimeout,\n    tls,\n    maxCachedSessions,\n    socketPath,\n    autoSelectFamily,\n    autoSelectFamilyAttemptTimeout,\n    allowH2,\n    clientTtl,\n    ...options\n  } = {}) {\n    if (connections != null && (!Number.isFinite(connections) || connections < 0)) {\n      throw new InvalidArgumentError('invalid connections')\n    }\n\n    if (typeof factory !== 'function') {\n      throw new InvalidArgumentError('factory must be a function.')\n    }\n\n    if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {\n      throw new InvalidArgumentError('connect must be a function or an object')\n    }\n\n    if (typeof connect !== 'function') {\n      connect = buildConnector({\n        ...tls,\n        maxCachedSessions,\n        allowH2,\n        socketPath,\n        timeout: connectTimeout,\n        ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),\n        ...connect\n      })\n    }\n\n    super()\n\n    this[kConnections] = connections || null\n    this[kUrl] = util.parseOrigin(origin)\n    this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }\n    this[kOptions].interceptors = options.interceptors\n      ? { ...options.interceptors }\n      : undefined\n    this[kFactory] = factory\n\n    this.on('connect', (origin, targets) => {\n      if (clientTtl != null && clientTtl > 0) {\n        for (const target of targets) {\n          Object.assign(target, { ttl: Date.now() })\n        }\n      }\n    })\n\n    this.on('connectionError', (origin, targets, error) => {\n      // If a connection error occurs, we remove the client from the pool,\n      // and emit a connectionError event. They will not be re-used.\n      // Fixes https://github.com/nodejs/undici/issues/3895\n      for (const target of targets) {\n        // Do not use kRemoveClient here, as it will close the client,\n        // but the client cannot be closed in this state.\n        const idx = this[kClients].indexOf(target)\n        if (idx !== -1) {\n          this[kClients].splice(idx, 1)\n        }\n      }\n    })\n  }\n\n  [kGetDispatcher] () {\n    const clientTtlOption = this[kOptions].clientTtl\n    for (const client of this[kClients]) {\n      // check ttl of client and if it's stale, remove it from the pool\n      if (clientTtlOption != null && clientTtlOption > 0 && client.ttl && ((Date.now() - client.ttl) > clientTtlOption)) {\n        this[kRemoveClient](client)\n      } else if (!client[kNeedDrain]) {\n        return client\n      }\n    }\n\n    if (!this[kConnections] || this[kClients].length < this[kConnections]) {\n      const dispatcher = this[kFactory](this[kUrl], this[kOptions])\n      this[kAddClient](dispatcher)\n      return dispatcher\n    }\n  }\n}\n\nmodule.exports = Pool\n"
  },
  {
    "path": "lib/dispatcher/proxy-agent.js",
    "content": "'use strict'\n\nconst { kProxy, kClose, kDestroy, kDispatch } = require('../core/symbols')\nconst Agent = require('./agent')\nconst Pool = require('./pool')\nconst DispatcherBase = require('./dispatcher-base')\nconst { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require('../core/errors')\nconst buildConnector = require('../core/connect')\nconst Client = require('./client')\nconst { channels } = require('../core/diagnostics')\nconst Socks5ProxyAgent = require('./socks5-proxy-agent')\n\nconst kAgent = Symbol('proxy agent')\nconst kClient = Symbol('proxy client')\nconst kProxyHeaders = Symbol('proxy headers')\nconst kRequestTls = Symbol('request tls settings')\nconst kProxyTls = Symbol('proxy tls settings')\nconst kConnectEndpoint = Symbol('connect endpoint function')\nconst kTunnelProxy = Symbol('tunnel proxy')\n\nfunction defaultProtocolPort (protocol) {\n  return protocol === 'https:' ? 443 : 80\n}\n\nfunction defaultFactory (origin, opts) {\n  return new Pool(origin, opts)\n}\n\nconst noop = () => {}\n\nfunction defaultAgentFactory (origin, opts) {\n  if (opts.connections === 1) {\n    return new Client(origin, opts)\n  }\n  return new Pool(origin, opts)\n}\n\nclass Http1ProxyWrapper extends DispatcherBase {\n  #client\n\n  constructor (proxyUrl, { headers = {}, connect, factory }) {\n    if (!proxyUrl) {\n      throw new InvalidArgumentError('Proxy URL is mandatory')\n    }\n\n    super()\n\n    this[kProxyHeaders] = headers\n    if (factory) {\n      this.#client = factory(proxyUrl, { connect })\n    } else {\n      this.#client = new Client(proxyUrl, { connect })\n    }\n  }\n\n  [kDispatch] (opts, handler) {\n    const onHeaders = handler.onHeaders\n    handler.onHeaders = function (statusCode, data, resume) {\n      if (statusCode === 407) {\n        if (typeof handler.onError === 'function') {\n          handler.onError(new InvalidArgumentError('Proxy Authentication Required (407)'))\n        }\n        return\n      }\n      if (onHeaders) onHeaders.call(this, statusCode, data, resume)\n    }\n\n    // Rewrite request as an HTTP1 Proxy request, without tunneling.\n    const {\n      origin,\n      path = '/',\n      headers = {}\n    } = opts\n\n    opts.path = origin + path\n\n    if (!('host' in headers) && !('Host' in headers)) {\n      const { host } = new URL(origin)\n      headers.host = host\n    }\n    opts.headers = { ...this[kProxyHeaders], ...headers }\n\n    return this.#client[kDispatch](opts, handler)\n  }\n\n  [kClose] () {\n    return this.#client.close()\n  }\n\n  [kDestroy] (err) {\n    return this.#client.destroy(err)\n  }\n}\n\nclass ProxyAgent extends DispatcherBase {\n  constructor (opts) {\n    if (!opts || (typeof opts === 'object' && !(opts instanceof URL) && !opts.uri)) {\n      throw new InvalidArgumentError('Proxy uri is mandatory')\n    }\n\n    const { clientFactory = defaultFactory } = opts\n    if (typeof clientFactory !== 'function') {\n      throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')\n    }\n\n    const { proxyTunnel = true } = opts\n\n    super()\n\n    const url = this.#getUrl(opts)\n    const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url\n\n    this[kProxy] = { uri: href, protocol }\n    this[kRequestTls] = opts.requestTls\n    this[kProxyTls] = opts.proxyTls\n    this[kProxyHeaders] = opts.headers || {}\n    this[kTunnelProxy] = proxyTunnel\n\n    if (opts.auth && opts.token) {\n      throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')\n    } else if (opts.auth) {\n      /* @deprecated in favour of opts.token */\n      this[kProxyHeaders]['proxy-authorization'] = `Basic ${opts.auth}`\n    } else if (opts.token) {\n      this[kProxyHeaders]['proxy-authorization'] = opts.token\n    } else if (username && password) {\n      this[kProxyHeaders]['proxy-authorization'] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString('base64')}`\n    }\n\n    const connect = buildConnector({ ...opts.proxyTls })\n    this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })\n\n    const agentFactory = opts.factory || defaultAgentFactory\n    const factory = (origin, options) => {\n      const { protocol } = new URL(origin)\n\n      // Handle SOCKS5 proxy\n      if (this[kProxy].protocol === 'socks5:' || this[kProxy].protocol === 'socks:') {\n        return new Socks5ProxyAgent(this[kProxy].uri, {\n          headers: this[kProxyHeaders],\n          connect,\n          factory: agentFactory,\n          username: opts.username || username,\n          password: opts.password || password,\n          proxyTls: opts.proxyTls\n        })\n      }\n\n      if (!this[kTunnelProxy] && protocol === 'http:' && this[kProxy].protocol === 'http:') {\n        return new Http1ProxyWrapper(this[kProxy].uri, {\n          headers: this[kProxyHeaders],\n          connect,\n          factory: agentFactory\n        })\n      }\n      return agentFactory(origin, options)\n    }\n\n    // For SOCKS5 proxies, we don't need a client to the proxy itself\n    // The SOCKS5 connection is handled within Socks5ProxyAgent\n    if (protocol === 'socks5:' || protocol === 'socks:') {\n      this[kClient] = null\n    } else {\n      this[kClient] = clientFactory(url, { connect })\n    }\n\n    this[kAgent] = new Agent({\n      ...opts,\n      factory,\n      connect: async (opts, callback) => {\n        // SOCKS5 proxies handle their own connections via Socks5ProxyAgent,\n        // so this connect function should never be called for them.\n        if (!this[kClient]) {\n          callback(new InvalidArgumentError('Cannot establish tunnel connection without a proxy client'))\n          return\n        }\n\n        let requestedPath = opts.host\n        if (!opts.port) {\n          requestedPath += `:${defaultProtocolPort(opts.protocol)}`\n        }\n        try {\n          const connectParams = {\n            origin,\n            port,\n            path: requestedPath,\n            signal: opts.signal,\n            headers: {\n              ...this[kProxyHeaders],\n              host: opts.host,\n              ...(opts.connections == null || opts.connections > 0 ? { 'proxy-connection': 'keep-alive' } : {})\n            },\n            servername: this[kProxyTls]?.servername || proxyHostname\n          }\n          const { socket, statusCode } = await this[kClient].connect(connectParams)\n          if (statusCode !== 200) {\n            socket.on('error', noop).destroy()\n            callback(new RequestAbortedError(`Proxy response (${statusCode}) !== 200 when HTTP Tunneling`))\n            return\n          }\n\n          if (channels.proxyConnected.hasSubscribers) {\n            channels.proxyConnected.publish({\n              socket,\n              connectParams\n            })\n          }\n\n          if (opts.protocol !== 'https:') {\n            callback(null, socket)\n            return\n          }\n          let servername\n          if (this[kRequestTls]) {\n            servername = this[kRequestTls].servername\n          } else {\n            servername = opts.servername\n          }\n          this[kConnectEndpoint]({ ...opts, servername, httpSocket: socket }, callback)\n        } catch (err) {\n          if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {\n            // Throw a custom error to avoid loop in client.js#connect\n            callback(new SecureProxyConnectionError(err))\n          } else {\n            callback(err)\n          }\n        }\n      }\n    })\n  }\n\n  dispatch (opts, handler) {\n    const headers = buildHeaders(opts.headers)\n    throwIfProxyAuthIsSent(headers)\n\n    if (headers && !('host' in headers) && !('Host' in headers)) {\n      const { host } = new URL(opts.origin)\n      headers.host = host\n    }\n\n    return this[kAgent].dispatch(\n      {\n        ...opts,\n        headers\n      },\n      handler\n    )\n  }\n\n  /**\n   * @param {import('../../types/proxy-agent').ProxyAgent.Options | string | URL} opts\n   * @returns {URL}\n   */\n  #getUrl (opts) {\n    if (typeof opts === 'string') {\n      return new URL(opts)\n    } else if (opts instanceof URL) {\n      return opts\n    } else {\n      return new URL(opts.uri)\n    }\n  }\n\n  [kClose] () {\n    const promises = [this[kAgent].close()]\n    if (this[kClient]) {\n      promises.push(this[kClient].close())\n    }\n    return Promise.all(promises)\n  }\n\n  [kDestroy] () {\n    const promises = [this[kAgent].destroy()]\n    if (this[kClient]) {\n      promises.push(this[kClient].destroy())\n    }\n    return Promise.all(promises)\n  }\n}\n\n/**\n * @param {string[] | Record<string, string>} headers\n * @returns {Record<string, string>}\n */\nfunction buildHeaders (headers) {\n  // When using undici.fetch, the headers list is stored\n  // as an array.\n  if (Array.isArray(headers)) {\n    /** @type {Record<string, string>} */\n    const headersPair = {}\n\n    for (let i = 0; i < headers.length; i += 2) {\n      headersPair[headers[i]] = headers[i + 1]\n    }\n\n    return headersPair\n  }\n\n  return headers\n}\n\n/**\n * @param {Record<string, string>} headers\n *\n * Previous versions of ProxyAgent suggests the Proxy-Authorization in request headers\n * Nevertheless, it was changed and to avoid a security vulnerability by end users\n * this check was created.\n * It should be removed in the next major version for performance reasons\n */\nfunction throwIfProxyAuthIsSent (headers) {\n  const existProxyAuth = headers && Object.keys(headers)\n    .find((key) => key.toLowerCase() === 'proxy-authorization')\n  if (existProxyAuth) {\n    throw new InvalidArgumentError('Proxy-Authorization should be sent in ProxyAgent constructor')\n  }\n}\n\nmodule.exports = ProxyAgent\n"
  },
  {
    "path": "lib/dispatcher/retry-agent.js",
    "content": "'use strict'\n\nconst Dispatcher = require('./dispatcher')\nconst RetryHandler = require('../handler/retry-handler')\n\nclass RetryAgent extends Dispatcher {\n  #agent = null\n  #options = null\n  constructor (agent, options = {}) {\n    super(options)\n    this.#agent = agent\n    this.#options = options\n  }\n\n  dispatch (opts, handler) {\n    const retry = new RetryHandler({\n      ...opts,\n      retryOptions: this.#options\n    }, {\n      dispatch: this.#agent.dispatch.bind(this.#agent),\n      handler\n    })\n    return this.#agent.dispatch(opts, retry)\n  }\n\n  close () {\n    return this.#agent.close()\n  }\n\n  destroy () {\n    return this.#agent.destroy()\n  }\n}\n\nmodule.exports = RetryAgent\n"
  },
  {
    "path": "lib/dispatcher/round-robin-pool.js",
    "content": "'use strict'\n\nconst {\n  PoolBase,\n  kClients,\n  kNeedDrain,\n  kAddClient,\n  kGetDispatcher,\n  kRemoveClient\n} = require('./pool-base')\nconst Client = require('./client')\nconst {\n  InvalidArgumentError\n} = require('../core/errors')\nconst util = require('../core/util')\nconst { kUrl } = require('../core/symbols')\nconst buildConnector = require('../core/connect')\n\nconst kOptions = Symbol('options')\nconst kConnections = Symbol('connections')\nconst kFactory = Symbol('factory')\nconst kIndex = Symbol('index')\n\nfunction defaultFactory (origin, opts) {\n  return new Client(origin, opts)\n}\n\nclass RoundRobinPool extends PoolBase {\n  constructor (origin, {\n    connections,\n    factory = defaultFactory,\n    connect,\n    connectTimeout,\n    tls,\n    maxCachedSessions,\n    socketPath,\n    autoSelectFamily,\n    autoSelectFamilyAttemptTimeout,\n    allowH2,\n    clientTtl,\n    ...options\n  } = {}) {\n    if (connections != null && (!Number.isFinite(connections) || connections < 0)) {\n      throw new InvalidArgumentError('invalid connections')\n    }\n\n    if (typeof factory !== 'function') {\n      throw new InvalidArgumentError('factory must be a function.')\n    }\n\n    if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {\n      throw new InvalidArgumentError('connect must be a function or an object')\n    }\n\n    if (typeof connect !== 'function') {\n      connect = buildConnector({\n        ...tls,\n        maxCachedSessions,\n        allowH2,\n        socketPath,\n        timeout: connectTimeout,\n        ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),\n        ...connect\n      })\n    }\n\n    super()\n\n    this[kConnections] = connections || null\n    this[kUrl] = util.parseOrigin(origin)\n    this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }\n    this[kOptions].interceptors = options.interceptors\n      ? { ...options.interceptors }\n      : undefined\n    this[kFactory] = factory\n    this[kIndex] = -1\n\n    this.on('connect', (origin, targets) => {\n      if (clientTtl != null && clientTtl > 0) {\n        for (const target of targets) {\n          Object.assign(target, { ttl: Date.now() })\n        }\n      }\n    })\n\n    this.on('connectionError', (origin, targets, error) => {\n      for (const target of targets) {\n        const idx = this[kClients].indexOf(target)\n        if (idx !== -1) {\n          this[kClients].splice(idx, 1)\n        }\n      }\n    })\n  }\n\n  [kGetDispatcher] () {\n    const clientTtlOption = this[kOptions].clientTtl\n    const clientsLength = this[kClients].length\n\n    // If we have no clients yet, create one\n    if (clientsLength === 0) {\n      const dispatcher = this[kFactory](this[kUrl], this[kOptions])\n      this[kAddClient](dispatcher)\n      return dispatcher\n    }\n\n    // Round-robin through existing clients\n    let checked = 0\n    while (checked < clientsLength) {\n      this[kIndex] = (this[kIndex] + 1) % clientsLength\n      const client = this[kClients][this[kIndex]]\n\n      // Check if client is stale (TTL expired)\n      if (clientTtlOption != null && clientTtlOption > 0 && client.ttl && ((Date.now() - client.ttl) > clientTtlOption)) {\n        this[kRemoveClient](client)\n        checked++\n        continue\n      }\n\n      // Return client if it's not draining\n      if (!client[kNeedDrain]) {\n        return client\n      }\n\n      checked++\n    }\n\n    // All clients are busy, create a new one if we haven't reached the limit\n    if (!this[kConnections] || clientsLength < this[kConnections]) {\n      const dispatcher = this[kFactory](this[kUrl], this[kOptions])\n      this[kAddClient](dispatcher)\n      return dispatcher\n    }\n  }\n}\n\nmodule.exports = RoundRobinPool\n"
  },
  {
    "path": "lib/dispatcher/socks5-proxy-agent.js",
    "content": "'use strict'\n\nconst net = require('node:net')\nconst { URL } = require('node:url')\n\nlet tls // include tls conditionally since it is not always available\nconst DispatcherBase = require('./dispatcher-base')\nconst { InvalidArgumentError } = require('../core/errors')\nconst { Socks5Client } = require('../core/socks5-client')\nconst { kDispatch, kClose, kDestroy } = require('../core/symbols')\nconst Pool = require('./pool')\nconst buildConnector = require('../core/connect')\nconst { debuglog } = require('node:util')\n\nconst debug = debuglog('undici:socks5-proxy')\n\nconst kProxyUrl = Symbol('proxy url')\nconst kProxyHeaders = Symbol('proxy headers')\nconst kProxyAuth = Symbol('proxy auth')\nconst kPool = Symbol('pool')\nconst kConnector = Symbol('connector')\n\n// Static flag to ensure warning is only emitted once per process\nlet experimentalWarningEmitted = false\n\n/**\n * SOCKS5 proxy agent for dispatching requests through a SOCKS5 proxy\n */\nclass Socks5ProxyAgent extends DispatcherBase {\n  constructor (proxyUrl, options = {}) {\n    super()\n\n    // Emit experimental warning only once\n    if (!experimentalWarningEmitted) {\n      process.emitWarning(\n        'SOCKS5 proxy support is experimental and subject to change',\n        'ExperimentalWarning'\n      )\n      experimentalWarningEmitted = true\n    }\n\n    if (!proxyUrl) {\n      throw new InvalidArgumentError('Proxy URL is mandatory')\n    }\n\n    // Parse proxy URL\n    const url = typeof proxyUrl === 'string' ? new URL(proxyUrl) : proxyUrl\n\n    if (url.protocol !== 'socks5:' && url.protocol !== 'socks:') {\n      throw new InvalidArgumentError('Proxy URL must use socks5:// or socks:// protocol')\n    }\n\n    this[kProxyUrl] = url\n    this[kProxyHeaders] = options.headers || {}\n\n    // Extract auth from URL or options\n    this[kProxyAuth] = {\n      username: options.username || (url.username ? decodeURIComponent(url.username) : null),\n      password: options.password || (url.password ? decodeURIComponent(url.password) : null)\n    }\n\n    // Create connector for proxy connection\n    this[kConnector] = options.connect || buildConnector({\n      ...options.proxyTls,\n      servername: options.proxyTls?.servername || url.hostname\n    })\n\n    // Pool for the actual HTTP connections (with SOCKS5 tunnel connect function)\n    this[kPool] = null\n  }\n\n  /**\n   * Create a SOCKS5 connection to the proxy\n   */\n  async createSocks5Connection (targetHost, targetPort) {\n    const proxyHost = this[kProxyUrl].hostname\n    const proxyPort = parseInt(this[kProxyUrl].port) || 1080\n\n    debug('creating SOCKS5 connection to', proxyHost, proxyPort)\n\n    // Connect to the SOCKS5 proxy\n    const socket = await new Promise((resolve, reject) => {\n      const onConnect = () => {\n        socket.removeListener('error', onError)\n        resolve(socket)\n      }\n\n      const onError = (err) => {\n        socket.removeListener('connect', onConnect)\n        reject(err)\n      }\n\n      const socket = net.connect({\n        host: proxyHost,\n        port: proxyPort\n      })\n\n      socket.once('connect', onConnect)\n      socket.once('error', onError)\n    })\n\n    // Create SOCKS5 client\n    const socks5Client = new Socks5Client(socket, this[kProxyAuth])\n\n    // Handle SOCKS5 errors\n    socks5Client.on('error', (err) => {\n      debug('SOCKS5 error:', err)\n      socket.destroy()\n    })\n\n    // Perform SOCKS5 handshake\n    await socks5Client.handshake()\n\n    // Wait for authentication (if required)\n    await new Promise((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        reject(new Error('SOCKS5 authentication timeout'))\n      }, 5000)\n\n      const onAuthenticated = () => {\n        clearTimeout(timeout)\n        socks5Client.removeListener('error', onError)\n        resolve()\n      }\n\n      const onError = (err) => {\n        clearTimeout(timeout)\n        socks5Client.removeListener('authenticated', onAuthenticated)\n        reject(err)\n      }\n\n      // Check if already authenticated (for NO_AUTH method)\n      if (socks5Client.state === 'authenticated') {\n        clearTimeout(timeout)\n        resolve()\n      } else {\n        socks5Client.once('authenticated', onAuthenticated)\n        socks5Client.once('error', onError)\n      }\n    })\n\n    // Send CONNECT command\n    await socks5Client.connect(targetHost, targetPort)\n\n    // Wait for connection\n    await new Promise((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        reject(new Error('SOCKS5 connection timeout'))\n      }, 5000)\n\n      const onConnected = (info) => {\n        debug('SOCKS5 tunnel established to', targetHost, targetPort, 'via', info)\n        clearTimeout(timeout)\n        socks5Client.removeListener('error', onError)\n        resolve()\n      }\n\n      const onError = (err) => {\n        clearTimeout(timeout)\n        socks5Client.removeListener('connected', onConnected)\n        reject(err)\n      }\n\n      socks5Client.once('connected', onConnected)\n      socks5Client.once('error', onError)\n    })\n\n    return socket\n  }\n\n  /**\n   * Dispatch a request through the SOCKS5 proxy\n   */\n  async [kDispatch] (opts, handler) {\n    const { origin } = opts\n\n    debug('dispatching request to', origin, 'via SOCKS5')\n\n    try {\n      // Create Pool with custom connect function if we don't have one yet\n      if (!this[kPool] || this[kPool].destroyed || this[kPool].closed) {\n        this[kPool] = new Pool(origin, {\n          pipelining: opts.pipelining,\n          connections: opts.connections,\n          connect: async (connectOpts, callback) => {\n            try {\n              const url = new URL(origin)\n              const targetHost = url.hostname\n              const targetPort = parseInt(url.port) || (url.protocol === 'https:' ? 443 : 80)\n\n              debug('establishing SOCKS5 connection to', targetHost, targetPort)\n\n              // Create SOCKS5 tunnel\n              const socket = await this.createSocks5Connection(targetHost, targetPort)\n\n              // Handle TLS if needed\n              let finalSocket = socket\n              if (url.protocol === 'https:') {\n                if (!tls) {\n                  tls = require('node:tls')\n                }\n                debug('upgrading to TLS')\n                finalSocket = tls.connect({\n                  socket,\n                  servername: targetHost,\n                  ...connectOpts.tls || {}\n                })\n\n                await new Promise((resolve, reject) => {\n                  finalSocket.once('secureConnect', resolve)\n                  finalSocket.once('error', reject)\n                })\n              }\n\n              callback(null, finalSocket)\n            } catch (err) {\n              debug('SOCKS5 connection error:', err)\n              callback(err)\n            }\n          }\n        })\n      }\n\n      // Dispatch the request through the pool\n      return this[kPool][kDispatch](opts, handler)\n    } catch (err) {\n      debug('dispatch error:', err)\n      if (typeof handler.onError === 'function') {\n        handler.onError(err)\n      } else {\n        throw err\n      }\n    }\n  }\n\n  async [kClose] () {\n    if (this[kPool]) {\n      await this[kPool].close()\n    }\n  }\n\n  async [kDestroy] (err) {\n    if (this[kPool]) {\n      await this[kPool].destroy(err)\n    }\n  }\n}\n\nmodule.exports = Socks5ProxyAgent\n"
  },
  {
    "path": "lib/encoding/index.js",
    "content": "'use strict'\n\nconst textDecoder = new TextDecoder()\n\n/**\n * @see https://encoding.spec.whatwg.org/#utf-8-decode\n * @param {Uint8Array} buffer\n */\nfunction utf8DecodeBytes (buffer) {\n  if (buffer.length === 0) {\n    return ''\n  }\n\n  // 1. Let buffer be the result of peeking three bytes from\n  //    ioQueue, converted to a byte sequence.\n\n  // 2. If buffer is 0xEF 0xBB 0xBF, then read three\n  //    bytes from ioQueue. (Do nothing with those bytes.)\n  if (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {\n    buffer = buffer.subarray(3)\n  }\n\n  // 3. Process a queue with an instance of UTF-8’s\n  //    decoder, ioQueue, output, and \"replacement\".\n  const output = textDecoder.decode(buffer)\n\n  // 4. Return output.\n  return output\n}\n\nmodule.exports = {\n  utf8DecodeBytes\n}\n"
  },
  {
    "path": "lib/global.js",
    "content": "'use strict'\n\n// We include a version number for the Dispatcher API. In case of breaking changes,\n// this version number must be increased to avoid conflicts.\nconst globalDispatcher = Symbol.for('undici.globalDispatcher.1')\nconst { InvalidArgumentError } = require('./core/errors')\nconst Agent = require('./dispatcher/agent')\n\nif (getGlobalDispatcher() === undefined) {\n  setGlobalDispatcher(new Agent())\n}\n\nfunction setGlobalDispatcher (agent) {\n  if (!agent || typeof agent.dispatch !== 'function') {\n    throw new InvalidArgumentError('Argument agent must implement Agent')\n  }\n  Object.defineProperty(globalThis, globalDispatcher, {\n    value: agent,\n    writable: true,\n    enumerable: false,\n    configurable: false\n  })\n}\n\nfunction getGlobalDispatcher () {\n  return globalThis[globalDispatcher]\n}\n\n// These are the globals that can be installed by undici.install().\n// Not exported by index.js to avoid use outside of this module.\nconst installedExports = /** @type {const} */ (\n  [\n    'fetch',\n    'Headers',\n    'Response',\n    'Request',\n    'FormData',\n    'WebSocket',\n    'CloseEvent',\n    'ErrorEvent',\n    'MessageEvent',\n    'EventSource'\n  ]\n)\n\nmodule.exports = {\n  setGlobalDispatcher,\n  getGlobalDispatcher,\n  installedExports\n}\n"
  },
  {
    "path": "lib/handler/cache-handler.js",
    "content": "'use strict'\n\nconst util = require('../core/util')\nconst {\n  parseCacheControlHeader,\n  parseVaryHeader,\n  isEtagUsable\n} = require('../util/cache')\nconst { parseHttpDate } = require('../util/date.js')\n\nfunction noop () {}\n\n// Status codes that we can use some heuristics on to cache\nconst HEURISTICALLY_CACHEABLE_STATUS_CODES = [\n  200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501\n]\n\n// Status codes which semantic is not handled by the cache\n// https://datatracker.ietf.org/doc/html/rfc9111#section-3\n// This list should not grow beyond 206 unless the RFC is updated\n// by a newer one including more. Please introduce another list if\n// implementing caching of responses with the 'must-understand' directive.\nconst NOT_UNDERSTOOD_STATUS_CODES = [\n  206\n]\n\nconst MAX_RESPONSE_AGE = 2147483647000\n\n/**\n * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler\n *\n * @implements {DispatchHandler}\n */\nclass CacheHandler {\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n   */\n  #cacheKey\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions['type']}\n   */\n  #cacheType\n\n  /**\n   * @type {number | undefined}\n   */\n  #cacheByDefault\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheStore}\n   */\n  #store\n\n  /**\n   * @type {import('../../types/dispatcher.d.ts').default.DispatchHandler}\n   */\n  #handler\n\n  /**\n   * @type {import('node:stream').Writable | undefined}\n   */\n  #writeStream\n\n  /**\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} opts\n   * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler\n   */\n  constructor ({ store, type, cacheByDefault }, cacheKey, handler) {\n    this.#store = store\n    this.#cacheType = type\n    this.#cacheByDefault = cacheByDefault\n    this.#cacheKey = cacheKey\n    this.#handler = handler\n  }\n\n  onRequestStart (controller, context) {\n    this.#writeStream?.destroy()\n    this.#writeStream = undefined\n    this.#handler.onRequestStart?.(controller, context)\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {number} statusCode\n   * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders\n   * @param {string} statusMessage\n   */\n  onResponseStart (\n    controller,\n    statusCode,\n    resHeaders,\n    statusMessage\n  ) {\n    const downstreamOnHeaders = () =>\n      this.#handler.onResponseStart?.(\n        controller,\n        statusCode,\n        resHeaders,\n        statusMessage\n      )\n    const handler = this\n\n    if (\n      !util.safeHTTPMethods.includes(this.#cacheKey.method) &&\n      statusCode >= 200 &&\n      statusCode <= 399\n    ) {\n      // Successful response to an unsafe method, delete it from cache\n      //  https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response\n      try {\n        this.#store.delete(this.#cacheKey)?.catch?.(noop)\n      } catch {\n        // Fail silently\n      }\n      return downstreamOnHeaders()\n    }\n\n    const cacheControlHeader = resHeaders['cache-control']\n    const heuristicallyCacheable = resHeaders['last-modified'] && HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode)\n    if (\n      !cacheControlHeader &&\n      !resHeaders['expires'] &&\n      !heuristicallyCacheable &&\n      !this.#cacheByDefault\n    ) {\n      // Don't have anything to tell us this response is cachable and we're not\n      //  caching by default\n      return downstreamOnHeaders()\n    }\n\n    const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {}\n    if (!canCacheResponse(this.#cacheType, statusCode, resHeaders, cacheControlDirectives)) {\n      return downstreamOnHeaders()\n    }\n\n    const now = Date.now()\n    const resAge = resHeaders.age ? getAge(resHeaders.age) : undefined\n    if (resAge && resAge >= MAX_RESPONSE_AGE) {\n      // Response considered stale\n      return downstreamOnHeaders()\n    }\n\n    const resDate = typeof resHeaders.date === 'string'\n      ? parseHttpDate(resHeaders.date)\n      : undefined\n\n    const staleAt =\n      determineStaleAt(this.#cacheType, now, resAge, resHeaders, resDate, cacheControlDirectives) ??\n      this.#cacheByDefault\n    if (staleAt === undefined || (resAge && resAge > staleAt)) {\n      return downstreamOnHeaders()\n    }\n\n    const baseTime = resDate ? resDate.getTime() : now\n    const absoluteStaleAt = staleAt + baseTime\n    if (now >= absoluteStaleAt) {\n      // Response is already stale\n      return downstreamOnHeaders()\n    }\n\n    let varyDirectives\n    if (this.#cacheKey.headers && resHeaders.vary) {\n      varyDirectives = parseVaryHeader(resHeaders.vary, this.#cacheKey.headers)\n      if (!varyDirectives) {\n        // Parse error\n        return downstreamOnHeaders()\n      }\n    }\n\n    const deleteAt = determineDeleteAt(baseTime, cacheControlDirectives, absoluteStaleAt)\n    const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives)\n\n    /**\n     * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n     */\n    const value = {\n      statusCode,\n      statusMessage,\n      headers: strippedHeaders,\n      vary: varyDirectives,\n      cacheControlDirectives,\n      cachedAt: resAge ? now - resAge : now,\n      staleAt: absoluteStaleAt,\n      deleteAt\n    }\n\n    // Not modified, re-use the cached value\n    // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-304-not-modified\n    if (statusCode === 304) {\n      const handle304 = (cachedValue) => {\n        if (!cachedValue) {\n          // Do not create a new cache entry, as a 304 won't have a body - so cannot be cached.\n          return downstreamOnHeaders()\n        }\n\n        // Re-use the cached value: statuscode, statusmessage, headers and body\n        value.statusCode = cachedValue.statusCode\n        value.statusMessage = cachedValue.statusMessage\n        value.etag = cachedValue.etag\n        value.headers = { ...cachedValue.headers, ...strippedHeaders }\n\n        downstreamOnHeaders()\n\n        this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)\n\n        if (!this.#writeStream || !cachedValue?.body) {\n          return\n        }\n\n        if (typeof cachedValue.body.values === 'function') {\n          const bodyIterator = cachedValue.body.values()\n\n          const streamCachedBody = () => {\n            for (const chunk of bodyIterator) {\n              const full = this.#writeStream.write(chunk) === false\n              this.#handler.onResponseData?.(controller, chunk)\n              // when stream is full stop writing until we get a 'drain' event\n              if (full) {\n                break\n              }\n            }\n          }\n\n          this.#writeStream\n            .on('error', function () {\n              handler.#writeStream = undefined\n              handler.#store.delete(handler.#cacheKey)\n            })\n            .on('drain', () => {\n              streamCachedBody()\n            })\n            .on('close', function () {\n              if (handler.#writeStream === this) {\n                handler.#writeStream = undefined\n              }\n            })\n\n          streamCachedBody()\n        } else if (typeof cachedValue.body.on === 'function') {\n          // Readable stream body (e.g. from async/remote cache stores)\n          cachedValue.body\n            .on('data', (chunk) => {\n              this.#writeStream.write(chunk)\n              this.#handler.onResponseData?.(controller, chunk)\n            })\n            .on('end', () => {\n              this.#writeStream.end()\n            })\n            .on('error', () => {\n              this.#writeStream = undefined\n              this.#store.delete(this.#cacheKey)\n            })\n\n          this.#writeStream\n            .on('error', function () {\n              handler.#writeStream = undefined\n              handler.#store.delete(handler.#cacheKey)\n            })\n            .on('close', function () {\n              if (handler.#writeStream === this) {\n                handler.#writeStream = undefined\n              }\n            })\n        }\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const result = this.#store.get(this.#cacheKey)\n      if (result && typeof result.then === 'function') {\n        result.then(handle304)\n      } else {\n        handle304(result)\n      }\n    } else {\n      if (typeof resHeaders.etag === 'string' && isEtagUsable(resHeaders.etag)) {\n        value.etag = resHeaders.etag\n      }\n\n      this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)\n\n      if (!this.#writeStream) {\n        return downstreamOnHeaders()\n      }\n\n      this.#writeStream\n        .on('drain', () => controller.resume())\n        .on('error', function () {\n          // TODO (fix): Make error somehow observable?\n          handler.#writeStream = undefined\n\n          // Delete the value in case the cache store is holding onto state from\n          //  the call to createWriteStream\n          handler.#store.delete(handler.#cacheKey)\n        })\n        .on('close', function () {\n          if (handler.#writeStream === this) {\n            handler.#writeStream = undefined\n          }\n\n          // TODO (fix): Should we resume even if was paused downstream?\n          controller.resume()\n        })\n\n      downstreamOnHeaders()\n    }\n  }\n\n  onResponseData (controller, chunk) {\n    if (this.#writeStream?.write(chunk) === false) {\n      controller.pause()\n    }\n\n    this.#handler.onResponseData?.(controller, chunk)\n  }\n\n  onResponseEnd (controller, trailers) {\n    this.#writeStream?.end()\n    this.#handler.onResponseEnd?.(controller, trailers)\n  }\n\n  onResponseError (controller, err) {\n    this.#writeStream?.destroy(err)\n    this.#writeStream = undefined\n    this.#handler.onResponseError?.(controller, err)\n  }\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen\n *\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType\n * @param {number} statusCode\n * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives\n */\nfunction canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirectives) {\n  // Status code must be final and understood.\n  if (statusCode < 200 || NOT_UNDERSTOOD_STATUS_CODES.includes(statusCode)) {\n    return false\n  }\n  // Responses with neither status codes that are heuristically cacheable, nor \"explicit enough\" caching\n  // directives, are not cacheable. \"Explicit enough\": see https://www.rfc-editor.org/rfc/rfc9111.html#section-3\n  if (!HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) && !resHeaders['expires'] &&\n    !cacheControlDirectives.public &&\n    cacheControlDirectives['max-age'] === undefined &&\n    // RFC 9111: a private response directive, if the cache is not shared\n    !(cacheControlDirectives.private && cacheType === 'private') &&\n    !(cacheControlDirectives['s-maxage'] !== undefined && cacheType === 'shared')\n  ) {\n    return false\n  }\n\n  if (cacheControlDirectives['no-store']) {\n    return false\n  }\n\n  if (cacheType === 'shared' && cacheControlDirectives.private === true) {\n    return false\n  }\n\n  // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1-5\n  if (resHeaders.vary?.includes('*')) {\n    return false\n  }\n\n  // https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen\n  if (resHeaders.authorization) {\n    if (!cacheControlDirectives.public || typeof resHeaders.authorization !== 'string') {\n      return false\n    }\n\n    if (\n      Array.isArray(cacheControlDirectives['no-cache']) &&\n      cacheControlDirectives['no-cache'].includes('authorization')\n    ) {\n      return false\n    }\n\n    if (\n      Array.isArray(cacheControlDirectives['private']) &&\n      cacheControlDirectives['private'].includes('authorization')\n    ) {\n      return false\n    }\n  }\n\n  return true\n}\n\n/**\n * @param {string | string[]} ageHeader\n * @returns {number | undefined}\n */\nfunction getAge (ageHeader) {\n  const age = parseInt(Array.isArray(ageHeader) ? ageHeader[0] : ageHeader)\n\n  return isNaN(age) ? undefined : age * 1000\n}\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType\n * @param {number} now\n * @param {number | undefined} age\n * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders\n * @param {Date | undefined} responseDate\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives\n *\n * @returns {number | undefined} time that the value is stale at in seconds or undefined if it shouldn't be cached\n */\nfunction determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheControlDirectives) {\n  if (cacheType === 'shared') {\n    // Prioritize s-maxage since we're a shared cache\n    //  s-maxage > max-age > Expire\n    //  https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10-3\n    const sMaxAge = cacheControlDirectives['s-maxage']\n    if (sMaxAge !== undefined) {\n      return sMaxAge > 0 ? sMaxAge * 1000 : undefined\n    }\n  }\n\n  const maxAge = cacheControlDirectives['max-age']\n  if (maxAge !== undefined) {\n    return maxAge > 0 ? maxAge * 1000 : undefined\n  }\n\n  if (typeof resHeaders.expires === 'string') {\n    // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3\n    const expiresDate = parseHttpDate(resHeaders.expires)\n    if (expiresDate) {\n      if (now >= expiresDate.getTime()) {\n        return undefined\n      }\n\n      if (responseDate) {\n        if (responseDate >= expiresDate) {\n          return undefined\n        }\n\n        if (age !== undefined && age > (expiresDate - responseDate)) {\n          return undefined\n        }\n      }\n\n      return expiresDate.getTime() - now\n    }\n  }\n\n  if (typeof resHeaders['last-modified'] === 'string') {\n    // https://www.rfc-editor.org/rfc/rfc9111.html#name-calculating-heuristic-fresh\n    const lastModified = new Date(resHeaders['last-modified'])\n    if (isValidDate(lastModified)) {\n      if (lastModified.getTime() >= now) {\n        return undefined\n      }\n\n      const responseAge = now - lastModified.getTime()\n\n      return responseAge * 0.1\n    }\n  }\n\n  if (cacheControlDirectives.immutable) {\n    // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2\n    return 31536000\n  }\n\n  return undefined\n}\n\n/**\n * @param {number} now\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives\n * @param {number} staleAt\n */\nfunction determineDeleteAt (now, cacheControlDirectives, staleAt) {\n  let staleWhileRevalidate = -Infinity\n  let staleIfError = -Infinity\n  let immutable = -Infinity\n\n  if (cacheControlDirectives['stale-while-revalidate']) {\n    staleWhileRevalidate = staleAt + (cacheControlDirectives['stale-while-revalidate'] * 1000)\n  }\n\n  if (cacheControlDirectives['stale-if-error']) {\n    staleIfError = staleAt + (cacheControlDirectives['stale-if-error'] * 1000)\n  }\n\n  if (cacheControlDirectives.immutable && staleWhileRevalidate === -Infinity && staleIfError === -Infinity) {\n    immutable = now + 31536000000\n  }\n\n  // When no stale directives or immutable flag, add a revalidation buffer\n  // equal to the freshness lifetime so the entry survives past staleAt long\n  // enough to be revalidated instead of silently disappearing.\n  if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity && immutable === -Infinity) {\n    const freshnessLifetime = staleAt - now\n    return staleAt + freshnessLifetime\n  }\n\n  return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable)\n}\n\n/**\n * Strips headers required to be removed in cached responses\n * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives\n * @returns {Record<string, string | string []>}\n */\nfunction stripNecessaryHeaders (resHeaders, cacheControlDirectives) {\n  const headersToRemove = [\n    'connection',\n    'proxy-authenticate',\n    'proxy-authentication-info',\n    'proxy-authorization',\n    'proxy-connection',\n    'te',\n    'transfer-encoding',\n    'upgrade',\n    // We'll add age back when serving it\n    'age'\n  ]\n\n  if (resHeaders['connection']) {\n    if (Array.isArray(resHeaders['connection'])) {\n      // connection: a\n      // connection: b\n      headersToRemove.push(...resHeaders['connection'].map(header => header.trim()))\n    } else {\n      // connection: a, b\n      headersToRemove.push(...resHeaders['connection'].split(',').map(header => header.trim()))\n    }\n  }\n\n  if (Array.isArray(cacheControlDirectives['no-cache'])) {\n    headersToRemove.push(...cacheControlDirectives['no-cache'])\n  }\n\n  if (Array.isArray(cacheControlDirectives['private'])) {\n    headersToRemove.push(...cacheControlDirectives['private'])\n  }\n\n  let strippedHeaders\n  for (const headerName of headersToRemove) {\n    if (resHeaders[headerName]) {\n      strippedHeaders ??= { ...resHeaders }\n      delete strippedHeaders[headerName]\n    }\n  }\n\n  return strippedHeaders ?? resHeaders\n}\n\n/**\n * @param {Date} date\n * @returns {boolean}\n */\nfunction isValidDate (date) {\n  return date instanceof Date && Number.isFinite(date.valueOf())\n}\n\nmodule.exports = CacheHandler\n"
  },
  {
    "path": "lib/handler/cache-revalidation-handler.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\n\n/**\n * This takes care of revalidation requests we send to the origin. If we get\n *  a response indicating that what we have is cached (via a HTTP 304), we can\n *  continue using the cached value. Otherwise, we'll receive the new response\n *  here, which we then just pass on to the next handler (most likely a\n *  CacheHandler). Note that this assumes the proper headers were already\n *  included in the request to tell the origin that we want to revalidate the\n *  response (i.e. if-modified-since or if-none-match).\n *\n * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-validation\n *\n * @implements {import('../../types/dispatcher.d.ts').default.DispatchHandler}\n */\nclass CacheRevalidationHandler {\n  #successful = false\n\n  /**\n   * @type {((boolean, any) => void) | null}\n   */\n  #callback\n\n  /**\n   * @type {(import('../../types/dispatcher.d.ts').default.DispatchHandler)}\n   */\n  #handler\n\n  #context\n\n  /**\n   * @type {boolean}\n   */\n  #allowErrorStatusCodes\n\n  /**\n   * @param {(boolean) => void} callback Function to call if the cached value is valid\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler\n   * @param {boolean} allowErrorStatusCodes\n   */\n  constructor (callback, handler, allowErrorStatusCodes) {\n    if (typeof callback !== 'function') {\n      throw new TypeError('callback must be a function')\n    }\n\n    this.#callback = callback\n    this.#handler = handler\n    this.#allowErrorStatusCodes = allowErrorStatusCodes\n  }\n\n  onRequestStart (_, context) {\n    this.#successful = false\n    this.#context = context\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  onResponseStart (\n    controller,\n    statusCode,\n    headers,\n    statusMessage\n  ) {\n    assert(this.#callback != null)\n\n    // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-a-validation-respo\n    // https://datatracker.ietf.org/doc/html/rfc5861#section-4\n    this.#successful = statusCode === 304 ||\n      (this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504)\n    this.#callback(this.#successful, this.#context)\n    this.#callback = null\n\n    if (this.#successful) {\n      return true\n    }\n\n    this.#handler.onRequestStart?.(controller, this.#context)\n    this.#handler.onResponseStart?.(\n      controller,\n      statusCode,\n      headers,\n      statusMessage\n    )\n  }\n\n  onResponseData (controller, chunk) {\n    if (this.#successful) {\n      return\n    }\n\n    return this.#handler.onResponseData?.(controller, chunk)\n  }\n\n  onResponseEnd (controller, trailers) {\n    if (this.#successful) {\n      return\n    }\n\n    this.#handler.onResponseEnd?.(controller, trailers)\n  }\n\n  onResponseError (controller, err) {\n    if (this.#successful) {\n      return\n    }\n\n    if (this.#callback) {\n      this.#callback(false)\n      this.#callback = null\n    }\n\n    if (typeof this.#handler.onResponseError === 'function') {\n      this.#handler.onResponseError(controller, err)\n    } else {\n      throw err\n    }\n  }\n}\n\nmodule.exports = CacheRevalidationHandler\n"
  },
  {
    "path": "lib/handler/decorator-handler.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst WrapHandler = require('./wrap-handler')\n\n/**\n * @deprecated\n */\nmodule.exports = class DecoratorHandler {\n  #handler\n  #onCompleteCalled = false\n  #onErrorCalled = false\n  #onResponseStartCalled = false\n\n  constructor (handler) {\n    if (typeof handler !== 'object' || handler === null) {\n      throw new TypeError('handler must be an object')\n    }\n    this.#handler = WrapHandler.wrap(handler)\n  }\n\n  onRequestStart (...args) {\n    this.#handler.onRequestStart?.(...args)\n  }\n\n  onRequestUpgrade (...args) {\n    assert(!this.#onCompleteCalled)\n    assert(!this.#onErrorCalled)\n\n    return this.#handler.onRequestUpgrade?.(...args)\n  }\n\n  onResponseStart (...args) {\n    assert(!this.#onCompleteCalled)\n    assert(!this.#onErrorCalled)\n    assert(!this.#onResponseStartCalled)\n\n    this.#onResponseStartCalled = true\n\n    return this.#handler.onResponseStart?.(...args)\n  }\n\n  onResponseData (...args) {\n    assert(!this.#onCompleteCalled)\n    assert(!this.#onErrorCalled)\n\n    return this.#handler.onResponseData?.(...args)\n  }\n\n  onResponseEnd (...args) {\n    assert(!this.#onCompleteCalled)\n    assert(!this.#onErrorCalled)\n\n    this.#onCompleteCalled = true\n    return this.#handler.onResponseEnd?.(...args)\n  }\n\n  onResponseError (...args) {\n    this.#onErrorCalled = true\n    return this.#handler.onResponseError?.(...args)\n  }\n\n  /**\n   * @deprecated\n   */\n  onBodySent () {}\n}\n"
  },
  {
    "path": "lib/handler/deduplication-handler.js",
    "content": "'use strict'\n\nconst { RequestAbortedError } = require('../core/errors')\n\n/**\n * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler\n */\n\nconst DEFAULT_MAX_BUFFER_SIZE = 5 * 1024 * 1024\n\n/**\n * @typedef {Object} WaitingHandler\n * @property {DispatchHandler} handler\n * @property {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n * @property {Buffer[]} bufferedChunks\n * @property {number} bufferedBytes\n * @property {object | null} pendingTrailers\n * @property {boolean} done\n */\n\n/**\n * Handler that forwards response events to multiple waiting handlers.\n * Used for request deduplication.\n *\n * @implements {DispatchHandler}\n */\nclass DeduplicationHandler {\n  /**\n   * @type {DispatchHandler}\n   */\n  #primaryHandler\n\n  /**\n   * @type {WaitingHandler[]}\n   */\n  #waitingHandlers = []\n\n  /**\n   * @type {number}\n   */\n  #maxBufferSize = DEFAULT_MAX_BUFFER_SIZE\n\n  /**\n   * @type {number}\n   */\n  #statusCode = 0\n\n  /**\n   * @type {Record<string, string | string[]>}\n   */\n  #headers = {}\n\n  /**\n   * @type {string}\n   */\n  #statusMessage = ''\n\n  /**\n   * @type {boolean}\n   */\n  #aborted = false\n\n  /**\n   * @type {boolean}\n   */\n  #responseStarted = false\n\n  /**\n   * @type {boolean}\n   */\n  #responseDataStarted = false\n\n  /**\n   * @type {boolean}\n   */\n  #completed = false\n\n  /**\n   * @type {import('../../types/dispatcher.d.ts').default.DispatchController | null}\n   */\n  #controller = null\n\n  /**\n   * @type {(() => void) | null}\n   */\n  #onComplete = null\n\n  /**\n   * @param {DispatchHandler} primaryHandler The primary handler\n   * @param {() => void} onComplete Callback when request completes\n   * @param {number} [maxBufferSize] Maximum paused buffer size per waiting handler\n   */\n  constructor (primaryHandler, onComplete, maxBufferSize = DEFAULT_MAX_BUFFER_SIZE) {\n    this.#primaryHandler = primaryHandler\n    this.#onComplete = onComplete\n    this.#maxBufferSize = maxBufferSize\n  }\n\n  /**\n   * Add a waiting handler that will receive response events.\n   * Returns false if deduplication can no longer safely attach this handler.\n   *\n   * @param {DispatchHandler} handler\n   * @returns {boolean}\n   */\n  addWaitingHandler (handler) {\n    if (this.#completed || this.#responseDataStarted) {\n      return false\n    }\n\n    const waitingHandler = this.#createWaitingHandler(handler)\n    const waitingController = waitingHandler.controller\n\n    try {\n      handler.onRequestStart?.(waitingController, null)\n\n      if (waitingController.aborted) {\n        waitingHandler.done = true\n        return true\n      }\n\n      if (this.#responseStarted) {\n        handler.onResponseStart?.(\n          waitingController,\n          this.#statusCode,\n          this.#headers,\n          this.#statusMessage\n        )\n      }\n    } catch {\n      // Ignore errors from waiting handlers\n      waitingHandler.done = true\n      return true\n    }\n\n    if (!waitingController.aborted) {\n      this.#waitingHandlers.push(waitingHandler)\n    }\n\n    return true\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {any} context\n   */\n  onRequestStart (controller, context) {\n    this.#controller = controller\n    this.#primaryHandler.onRequestStart?.(controller, context)\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {number} statusCode\n   * @param {import('../../types/header.d.ts').IncomingHttpHeaders} headers\n   * @param {Socket} socket\n   */\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.#primaryHandler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {number} statusCode\n   * @param {Record<string, string | string[]>} headers\n   * @param {string} statusMessage\n   */\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    this.#responseStarted = true\n    this.#statusCode = statusCode\n    this.#headers = headers\n    this.#statusMessage = statusMessage\n\n    this.#primaryHandler.onResponseStart?.(controller, statusCode, headers, statusMessage)\n\n    for (const waitingHandler of this.#waitingHandlers) {\n      const { handler, controller: waitingController } = waitingHandler\n\n      if (waitingHandler.done || waitingController.aborted) {\n        waitingHandler.done = true\n        continue\n      }\n\n      try {\n        handler.onResponseStart?.(\n          waitingController,\n          statusCode,\n          headers,\n          statusMessage\n        )\n      } catch {\n        // Ignore errors from waiting handlers\n      }\n\n      if (waitingController.aborted) {\n        waitingHandler.done = true\n      }\n    }\n\n    this.#pruneDoneWaitingHandlers()\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {Buffer} chunk\n   */\n  onResponseData (controller, chunk) {\n    if (this.#aborted || this.#completed) {\n      return\n    }\n\n    this.#responseDataStarted = true\n\n    this.#primaryHandler.onResponseData?.(controller, chunk)\n\n    for (const waitingHandler of this.#waitingHandlers) {\n      const { handler, controller: waitingController } = waitingHandler\n\n      if (waitingHandler.done || waitingController.aborted) {\n        waitingHandler.done = true\n        continue\n      }\n\n      if (waitingController.paused) {\n        this.#bufferWaitingChunk(waitingHandler, chunk)\n        continue\n      }\n\n      try {\n        handler.onResponseData?.(waitingController, chunk)\n      } catch {\n        // Ignore errors from waiting handlers\n      }\n\n      if (waitingController.aborted) {\n        waitingHandler.done = true\n        waitingHandler.bufferedChunks = []\n        waitingHandler.bufferedBytes = 0\n      }\n    }\n\n    this.#pruneDoneWaitingHandlers()\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {object} trailers\n   */\n  onResponseEnd (controller, trailers) {\n    if (this.#aborted || this.#completed) {\n      return\n    }\n\n    this.#completed = true\n    this.#primaryHandler.onResponseEnd?.(controller, trailers)\n\n    for (const waitingHandler of this.#waitingHandlers) {\n      if (waitingHandler.done || waitingHandler.controller.aborted) {\n        waitingHandler.done = true\n        continue\n      }\n\n      this.#flushWaitingHandler(waitingHandler)\n\n      if (waitingHandler.done || waitingHandler.controller.aborted) {\n        waitingHandler.done = true\n        continue\n      }\n\n      if (waitingHandler.controller.paused && waitingHandler.bufferedChunks.length > 0) {\n        waitingHandler.pendingTrailers = trailers\n        continue\n      }\n\n      try {\n        waitingHandler.handler.onResponseEnd?.(waitingHandler.controller, trailers)\n      } catch {\n        // Ignore errors from waiting handlers\n      }\n\n      waitingHandler.done = true\n    }\n\n    this.#pruneDoneWaitingHandlers()\n    this.#onComplete?.()\n  }\n\n  /**\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller\n   * @param {Error} err\n   */\n  onResponseError (controller, err) {\n    if (this.#completed) {\n      return\n    }\n\n    this.#aborted = true\n    this.#completed = true\n\n    this.#primaryHandler.onResponseError?.(controller, err)\n\n    for (const waitingHandler of this.#waitingHandlers) {\n      this.#errorWaitingHandler(waitingHandler, err)\n    }\n\n    this.#waitingHandlers = []\n    this.#onComplete?.()\n  }\n\n  /**\n   * @param {DispatchHandler} handler\n   * @returns {WaitingHandler}\n   */\n  #createWaitingHandler (handler) {\n    /** @type {WaitingHandler} */\n    const waitingHandler = {\n      handler,\n      controller: null,\n      bufferedChunks: [],\n      bufferedBytes: 0,\n      pendingTrailers: null,\n      done: false\n    }\n\n    const state = {\n      aborted: false,\n      paused: false,\n      reason: null\n    }\n\n    waitingHandler.controller = {\n      resume: () => {\n        if (state.aborted) {\n          return\n        }\n\n        state.paused = false\n        this.#flushWaitingHandler(waitingHandler)\n\n        if (\n          this.#completed &&\n          waitingHandler.pendingTrailers &&\n          waitingHandler.bufferedChunks.length === 0 &&\n          !state.paused &&\n          !state.aborted\n        ) {\n          try {\n            waitingHandler.handler.onResponseEnd?.(waitingHandler.controller, waitingHandler.pendingTrailers)\n          } catch {\n            // Ignore errors from waiting handlers\n          }\n\n          waitingHandler.pendingTrailers = null\n          waitingHandler.done = true\n        }\n\n        this.#pruneDoneWaitingHandlers()\n      },\n      pause: () => {\n        if (!state.aborted) {\n          state.paused = true\n        }\n      },\n      get paused () { return state.paused },\n      get aborted () { return state.aborted },\n      get reason () { return state.reason },\n      abort: (reason) => {\n        state.aborted = true\n        state.reason = reason ?? null\n        waitingHandler.done = true\n        waitingHandler.pendingTrailers = null\n        waitingHandler.bufferedChunks = []\n        waitingHandler.bufferedBytes = 0\n      }\n    }\n\n    return waitingHandler\n  }\n\n  /**\n   * @param {WaitingHandler} waitingHandler\n   * @param {Buffer} chunk\n   */\n  #bufferWaitingChunk (waitingHandler, chunk) {\n    if (waitingHandler.done || waitingHandler.controller.aborted) {\n      waitingHandler.done = true\n      waitingHandler.bufferedChunks = []\n      waitingHandler.bufferedBytes = 0\n      return\n    }\n\n    const bufferedChunk = Buffer.from(chunk)\n    waitingHandler.bufferedChunks.push(bufferedChunk)\n    waitingHandler.bufferedBytes += bufferedChunk.length\n\n    if (waitingHandler.bufferedBytes > this.#maxBufferSize) {\n      const err = new RequestAbortedError(`Deduplicated waiting handler exceeded maxBufferSize (${this.#maxBufferSize} bytes) while paused`)\n      this.#errorWaitingHandler(waitingHandler, err)\n    }\n  }\n\n  /**\n   * @param {WaitingHandler} waitingHandler\n   */\n  #flushWaitingHandler (waitingHandler) {\n    const { handler, controller } = waitingHandler\n\n    while (\n      !waitingHandler.done &&\n      !controller.aborted &&\n      !controller.paused &&\n      waitingHandler.bufferedChunks.length > 0\n    ) {\n      const bufferedChunk = waitingHandler.bufferedChunks.shift()\n      waitingHandler.bufferedBytes -= bufferedChunk.length\n\n      try {\n        handler.onResponseData?.(controller, bufferedChunk)\n      } catch {\n        // Ignore errors from waiting handlers\n      }\n\n      if (controller.aborted) {\n        waitingHandler.done = true\n        waitingHandler.pendingTrailers = null\n        waitingHandler.bufferedChunks = []\n        waitingHandler.bufferedBytes = 0\n        break\n      }\n    }\n  }\n\n  /**\n   * @param {WaitingHandler} waitingHandler\n   * @param {Error} err\n   */\n  #errorWaitingHandler (waitingHandler, err) {\n    if (waitingHandler.done) {\n      return\n    }\n\n    waitingHandler.done = true\n    waitingHandler.pendingTrailers = null\n    waitingHandler.bufferedChunks = []\n    waitingHandler.bufferedBytes = 0\n\n    try {\n      waitingHandler.controller.abort(err)\n      waitingHandler.handler.onResponseError?.(waitingHandler.controller, err)\n    } catch {\n      // Ignore errors from waiting handlers\n    }\n  }\n\n  #pruneDoneWaitingHandlers () {\n    this.#waitingHandlers = this.#waitingHandlers.filter(waitingHandler => waitingHandler.done === false)\n  }\n}\n\nmodule.exports = DeduplicationHandler\n"
  },
  {
    "path": "lib/handler/redirect-handler.js",
    "content": "'use strict'\n\nconst util = require('../core/util')\nconst { kBodyUsed } = require('../core/symbols')\nconst assert = require('node:assert')\nconst { InvalidArgumentError } = require('../core/errors')\nconst EE = require('node:events')\n\nconst redirectableStatusCodes = [300, 301, 302, 303, 307, 308]\n\nconst kBody = Symbol('body')\n\nconst noop = () => {}\n\nclass BodyAsyncIterable {\n  constructor (body) {\n    this[kBody] = body\n    this[kBodyUsed] = false\n  }\n\n  async * [Symbol.asyncIterator] () {\n    assert(!this[kBodyUsed], 'disturbed')\n    this[kBodyUsed] = true\n    yield * this[kBody]\n  }\n}\n\nclass RedirectHandler {\n  static buildDispatch (dispatcher, maxRedirections) {\n    if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {\n      throw new InvalidArgumentError('maxRedirections must be a positive number')\n    }\n\n    const dispatch = dispatcher.dispatch.bind(dispatcher)\n    return (opts, originalHandler) => dispatch(opts, new RedirectHandler(dispatch, maxRedirections, opts, originalHandler))\n  }\n\n  constructor (dispatch, maxRedirections, opts, handler) {\n    if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {\n      throw new InvalidArgumentError('maxRedirections must be a positive number')\n    }\n\n    this.dispatch = dispatch\n    this.location = null\n    const { maxRedirections: _, ...cleanOpts } = opts\n    this.opts = cleanOpts // opts must be a copy, exclude maxRedirections\n    this.maxRedirections = maxRedirections\n    this.handler = handler\n    this.history = []\n\n    if (util.isStream(this.opts.body)) {\n      // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp\n      // so that it can be dispatched again?\n      // TODO (fix): Do we need 100-expect support to provide a way to do this properly?\n      if (util.bodyLength(this.opts.body) === 0) {\n        this.opts.body\n          .on('data', function () {\n            assert(false)\n          })\n      }\n\n      if (typeof this.opts.body.readableDidRead !== 'boolean') {\n        this.opts.body[kBodyUsed] = false\n        EE.prototype.on.call(this.opts.body, 'data', function () {\n          this[kBodyUsed] = true\n        })\n      }\n    } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {\n      // TODO (fix): We can't access ReadableStream internal state\n      // to determine whether or not it has been disturbed. This is just\n      // a workaround.\n      this.opts.body = new BodyAsyncIterable(this.opts.body)\n    } else if (\n      this.opts.body &&\n      typeof this.opts.body !== 'string' &&\n      !ArrayBuffer.isView(this.opts.body) &&\n      util.isIterable(this.opts.body) &&\n      !util.isFormDataLike(this.opts.body)\n    ) {\n      // TODO: Should we allow re-using iterable if !this.opts.idempotent\n      // or through some other flag?\n      this.opts.body = new BodyAsyncIterable(this.opts.body)\n    }\n  }\n\n  onRequestStart (controller, context) {\n    this.handler.onRequestStart?.(controller, { ...context, history: this.history })\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {\n      throw new Error('max redirects')\n    }\n\n    // https://tools.ietf.org/html/rfc7231#section-6.4.2\n    // https://fetch.spec.whatwg.org/#http-redirect-fetch\n    // In case of HTTP 301 or 302 with POST, change the method to GET\n    if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') {\n      this.opts.method = 'GET'\n      if (util.isStream(this.opts.body)) {\n        util.destroy(this.opts.body.on('error', noop))\n      }\n      this.opts.body = null\n    }\n\n    // https://tools.ietf.org/html/rfc7231#section-6.4.4\n    // In case of HTTP 303, always replace method to be either HEAD or GET\n    if (statusCode === 303 && this.opts.method !== 'HEAD') {\n      this.opts.method = 'GET'\n      if (util.isStream(this.opts.body)) {\n        util.destroy(this.opts.body.on('error', noop))\n      }\n      this.opts.body = null\n    }\n\n    this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body) || redirectableStatusCodes.indexOf(statusCode) === -1\n      ? null\n      : headers.location\n\n    if (this.opts.origin) {\n      this.history.push(new URL(this.opts.path, this.opts.origin))\n    }\n\n    if (!this.location) {\n      this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)\n      return\n    }\n\n    const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))\n    const path = search ? `${pathname}${search}` : pathname\n\n    // Check for redirect loops by seeing if we've already visited this URL in our history\n    // This catches the case where Client/Pool try to handle cross-origin redirects but fail\n    // and keep redirecting to the same URL in an infinite loop\n    const redirectUrlString = `${origin}${path}`\n    for (const historyUrl of this.history) {\n      if (historyUrl.toString() === redirectUrlString) {\n        throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`)\n      }\n    }\n\n    // Remove headers referring to the original URL.\n    // By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.\n    // https://tools.ietf.org/html/rfc7231#section-6.4\n    this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin)\n    this.opts.path = path\n    this.opts.origin = origin\n    this.opts.query = null\n  }\n\n  onResponseData (controller, chunk) {\n    if (this.location) {\n      /*\n        https://tools.ietf.org/html/rfc7231#section-6.4\n\n        TLDR: undici always ignores 3xx response bodies.\n\n        Redirection is used to serve the requested resource from another URL, so it assumes that\n        no body is generated (and thus can be ignored). Even though generating a body is not prohibited.\n\n        For status 301, 302, 303, 307 and 308 (the latter from RFC 7238), the specs mention that the body usually\n        (which means it's optional and not mandated) contain just an hyperlink to the value of\n        the Location response header, so the body can be ignored safely.\n\n        For status 300, which is \"Multiple Choices\", the spec mentions both generating a Location\n        response header AND a response body with the other possible location to follow.\n        Since the spec explicitly chooses not to specify a format for such body and leave it to\n        servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.\n      */\n    } else {\n      this.handler.onResponseData?.(controller, chunk)\n    }\n  }\n\n  onResponseEnd (controller, trailers) {\n    if (this.location) {\n      /*\n        https://tools.ietf.org/html/rfc7231#section-6.4\n\n        TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections\n        and neither are useful if present.\n\n        See comment on onData method above for more detailed information.\n      */\n      this.dispatch(this.opts, this)\n    } else {\n      this.handler.onResponseEnd(controller, trailers)\n    }\n  }\n\n  onResponseError (controller, error) {\n    this.handler.onResponseError?.(controller, error)\n  }\n}\n\n// https://tools.ietf.org/html/rfc7231#section-6.4.4\nfunction shouldRemoveHeader (header, removeContent, unknownOrigin) {\n  if (header.length === 4) {\n    return util.headerNameToString(header) === 'host'\n  }\n  if (removeContent && util.headerNameToString(header).startsWith('content-')) {\n    return true\n  }\n  if (unknownOrigin && (header.length === 13 || header.length === 6 || header.length === 19)) {\n    const name = util.headerNameToString(header)\n    return name === 'authorization' || name === 'cookie' || name === 'proxy-authorization'\n  }\n  return false\n}\n\n// https://tools.ietf.org/html/rfc7231#section-6.4\nfunction cleanRequestHeaders (headers, removeContent, unknownOrigin) {\n  const ret = []\n  if (Array.isArray(headers)) {\n    for (let i = 0; i < headers.length; i += 2) {\n      if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) {\n        ret.push(headers[i], headers[i + 1])\n      }\n    }\n  } else if (headers && typeof headers === 'object') {\n    const entries = util.hasSafeIterator(headers) ? headers : Object.entries(headers)\n\n    for (const [key, value] of entries) {\n      if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {\n        ret.push(key, value)\n      }\n    }\n  } else {\n    assert(headers == null, 'headers must be an object or an array')\n  }\n  return ret\n}\n\nmodule.exports = RedirectHandler\n"
  },
  {
    "path": "lib/handler/retry-handler.js",
    "content": "'use strict'\nconst assert = require('node:assert')\n\nconst { kRetryHandlerDefaultRetry } = require('../core/symbols')\nconst { RequestRetryError } = require('../core/errors')\nconst WrapHandler = require('./wrap-handler')\nconst {\n  isDisturbed,\n  parseRangeHeader,\n  wrapRequestBody\n} = require('../core/util')\n\nfunction calculateRetryAfterHeader (retryAfter) {\n  const retryTime = new Date(retryAfter).getTime()\n  return isNaN(retryTime) ? 0 : retryTime - Date.now()\n}\n\nclass RetryHandler {\n  constructor (opts, { dispatch, handler }) {\n    const { retryOptions, ...dispatchOpts } = opts\n    const {\n      // Retry scoped\n      retry: retryFn,\n      maxRetries,\n      maxTimeout,\n      minTimeout,\n      timeoutFactor,\n      // Response scoped\n      methods,\n      errorCodes,\n      retryAfter,\n      statusCodes,\n      throwOnError\n    } = retryOptions ?? {}\n\n    this.error = null\n    this.dispatch = dispatch\n    this.handler = WrapHandler.wrap(handler)\n    this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }\n    this.retryOpts = {\n      throwOnError: throwOnError ?? true,\n      retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],\n      retryAfter: retryAfter ?? true,\n      maxTimeout: maxTimeout ?? 30 * 1000, // 30s,\n      minTimeout: minTimeout ?? 500, // .5s\n      timeoutFactor: timeoutFactor ?? 2,\n      maxRetries: maxRetries ?? 5,\n      // What errors we should retry\n      methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],\n      // Indicates which errors to retry\n      statusCodes: statusCodes ?? [500, 502, 503, 504, 429],\n      // List of errors to retry\n      errorCodes: errorCodes ?? [\n        'ECONNRESET',\n        'ECONNREFUSED',\n        'ENOTFOUND',\n        'ENETDOWN',\n        'ENETUNREACH',\n        'EHOSTDOWN',\n        'EHOSTUNREACH',\n        'EPIPE',\n        'UND_ERR_SOCKET'\n      ]\n    }\n\n    this.retryCount = 0\n    this.retryCountCheckpoint = 0\n    this.headersSent = false\n    this.start = 0\n    this.end = null\n    this.etag = null\n  }\n\n  onResponseStartWithRetry (controller, statusCode, headers, statusMessage, err) {\n    if (this.retryOpts.throwOnError) {\n      // Preserve old behavior for status codes that are not eligible for retry\n      if (this.retryOpts.statusCodes.includes(statusCode) === false) {\n        this.headersSent = true\n        this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)\n      } else {\n        this.error = err\n      }\n\n      return\n    }\n\n    if (isDisturbed(this.opts.body)) {\n      this.headersSent = true\n      this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)\n      return\n    }\n\n    function shouldRetry (passedErr) {\n      if (passedErr) {\n        this.headersSent = true\n        this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)\n        controller.resume()\n        return\n      }\n\n      this.error = err\n      controller.resume()\n    }\n\n    controller.pause()\n    this.retryOpts.retry(\n      err,\n      {\n        state: { counter: this.retryCount },\n        opts: { retryOptions: this.retryOpts, ...this.opts }\n      },\n      shouldRetry.bind(this)\n    )\n  }\n\n  onRequestStart (controller, context) {\n    if (!this.headersSent) {\n      this.handler.onRequestStart?.(controller, context)\n    }\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {\n    const { statusCode, code, headers } = err\n    const { method, retryOptions } = opts\n    const {\n      maxRetries,\n      minTimeout,\n      maxTimeout,\n      timeoutFactor,\n      statusCodes,\n      errorCodes,\n      methods\n    } = retryOptions\n    const { counter } = state\n\n    // Any code that is not a Undici's originated and allowed to retry\n    if (code && code !== 'UND_ERR_REQ_RETRY' && !errorCodes.includes(code)) {\n      cb(err)\n      return\n    }\n\n    // If a set of method are provided and the current method is not in the list\n    if (Array.isArray(methods) && !methods.includes(method)) {\n      cb(err)\n      return\n    }\n\n    // If a set of status code are provided and the current status code is not in the list\n    if (\n      statusCode != null &&\n      Array.isArray(statusCodes) &&\n      !statusCodes.includes(statusCode)\n    ) {\n      cb(err)\n      return\n    }\n\n    // If we reached the max number of retries\n    if (counter > maxRetries) {\n      cb(err)\n      return\n    }\n\n    let retryAfterHeader = headers?.['retry-after']\n    if (retryAfterHeader) {\n      retryAfterHeader = Number(retryAfterHeader)\n      retryAfterHeader = Number.isNaN(retryAfterHeader)\n        ? calculateRetryAfterHeader(headers['retry-after'])\n        : retryAfterHeader * 1e3 // Retry-After is in seconds\n    }\n\n    const retryTimeout =\n      retryAfterHeader > 0\n        ? Math.min(retryAfterHeader, maxTimeout)\n        : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)\n\n    setTimeout(() => cb(null), retryTimeout)\n  }\n\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    this.error = null\n    this.retryCount += 1\n\n    if (statusCode >= 300) {\n      const err = new RequestRetryError('Request failed', statusCode, {\n        headers,\n        data: {\n          count: this.retryCount\n        }\n      })\n\n      this.onResponseStartWithRetry(controller, statusCode, headers, statusMessage, err)\n      return\n    }\n\n    // Checkpoint for resume from where we left it\n    if (this.headersSent) {\n      // Only Partial Content 206 supposed to provide Content-Range,\n      // any other status code that partially consumed the payload\n      // should not be retried because it would result in downstream\n      // wrongly concatenate multiple responses.\n      if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {\n        throw new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {\n          headers,\n          data: { count: this.retryCount }\n        })\n      }\n\n      const contentRange = parseRangeHeader(headers['content-range'])\n      // If no content range\n      if (!contentRange) {\n        // We always throw here as we want to indicate that we entred unexpected path\n        throw new RequestRetryError('Content-Range mismatch', statusCode, {\n          headers,\n          data: { count: this.retryCount }\n        })\n      }\n\n      // Let's start with a weak etag check\n      if (this.etag != null && this.etag !== headers.etag) {\n        // We always throw here as we want to indicate that we entred unexpected path\n        throw new RequestRetryError('ETag mismatch', statusCode, {\n          headers,\n          data: { count: this.retryCount }\n        })\n      }\n\n      const { start, size, end = size ? size - 1 : null } = contentRange\n\n      assert(this.start === start, 'content-range mismatch')\n      assert(this.end == null || this.end === end, 'content-range mismatch')\n\n      return\n    }\n\n    if (this.end == null) {\n      if (statusCode === 206) {\n        // First time we receive 206\n        const range = parseRangeHeader(headers['content-range'])\n\n        if (range == null) {\n          this.headersSent = true\n          this.handler.onResponseStart?.(\n            controller,\n            statusCode,\n            headers,\n            statusMessage\n          )\n          return\n        }\n\n        const { start, size, end = size ? size - 1 : null } = range\n        assert(\n          start != null && Number.isFinite(start),\n          'content-range mismatch'\n        )\n        assert(end != null && Number.isFinite(end), 'invalid content-length')\n\n        this.start = start\n        this.end = end\n      }\n\n      // We make our best to checkpoint the body for further range headers\n      if (this.end == null) {\n        const contentLength = headers['content-length']\n        this.end = contentLength != null ? Number(contentLength) - 1 : null\n      }\n\n      assert(Number.isFinite(this.start))\n      assert(\n        this.end == null || Number.isFinite(this.end),\n        'invalid content-length'\n      )\n\n      this.resume = true\n      this.etag = headers.etag != null ? headers.etag : null\n\n      // Weak etags are not useful for comparison nor cache\n      // for instance not safe to assume if the response is byte-per-byte\n      // equal\n      if (\n        this.etag != null &&\n        this.etag[0] === 'W' &&\n        this.etag[1] === '/'\n      ) {\n        this.etag = null\n      }\n\n      this.headersSent = true\n      this.handler.onResponseStart?.(\n        controller,\n        statusCode,\n        headers,\n        statusMessage\n      )\n    } else {\n      throw new RequestRetryError('Request failed', statusCode, {\n        headers,\n        data: { count: this.retryCount }\n      })\n    }\n  }\n\n  onResponseData (controller, chunk) {\n    if (this.error) {\n      return\n    }\n\n    this.start += chunk.length\n\n    this.handler.onResponseData?.(controller, chunk)\n  }\n\n  onResponseEnd (controller, trailers) {\n    if (this.error && this.retryOpts.throwOnError) {\n      throw this.error\n    }\n\n    if (!this.error) {\n      this.retryCount = 0\n      return this.handler.onResponseEnd?.(controller, trailers)\n    }\n\n    this.retry(controller)\n  }\n\n  retry (controller) {\n    if (this.start !== 0) {\n      const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }\n\n      // Weak etag check - weak etags will make comparison algorithms never match\n      if (this.etag != null) {\n        headers['if-match'] = this.etag\n      }\n\n      this.opts = {\n        ...this.opts,\n        headers: {\n          ...this.opts.headers,\n          ...headers\n        }\n      }\n    }\n\n    try {\n      this.retryCountCheckpoint = this.retryCount\n      this.dispatch(this.opts, this)\n    } catch (err) {\n      this.handler.onResponseError?.(controller, err)\n    }\n  }\n\n  onResponseError (controller, err) {\n    if (controller?.aborted || isDisturbed(this.opts.body)) {\n      this.handler.onResponseError?.(controller, err)\n      return\n    }\n\n    function shouldRetry (returnedErr) {\n      if (!returnedErr) {\n        this.retry(controller)\n        return\n      }\n\n      this.handler?.onResponseError?.(controller, returnedErr)\n    }\n\n    // We reconcile in case of a mix between network errors\n    // and server error response\n    if (this.retryCount - this.retryCountCheckpoint > 0) {\n      // We count the difference between the last checkpoint and the current retry count\n      this.retryCount =\n        this.retryCountCheckpoint +\n        (this.retryCount - this.retryCountCheckpoint)\n    } else {\n      this.retryCount += 1\n    }\n\n    this.retryOpts.retry(\n      err,\n      {\n        state: { counter: this.retryCount },\n        opts: { retryOptions: this.retryOpts, ...this.opts }\n      },\n      shouldRetry.bind(this)\n    )\n  }\n}\n\nmodule.exports = RetryHandler\n"
  },
  {
    "path": "lib/handler/unwrap-handler.js",
    "content": "'use strict'\n\nconst { parseHeaders } = require('../core/util')\nconst { InvalidArgumentError } = require('../core/errors')\n\nconst kResume = Symbol('resume')\n\nclass UnwrapController {\n  #paused = false\n  #reason = null\n  #aborted = false\n  #abort\n\n  [kResume] = null\n\n  constructor (abort) {\n    this.#abort = abort\n  }\n\n  pause () {\n    this.#paused = true\n  }\n\n  resume () {\n    if (this.#paused) {\n      this.#paused = false\n      this[kResume]?.()\n    }\n  }\n\n  abort (reason) {\n    if (!this.#aborted) {\n      this.#aborted = true\n      this.#reason = reason\n      this.#abort(reason)\n    }\n  }\n\n  get aborted () {\n    return this.#aborted\n  }\n\n  get reason () {\n    return this.#reason\n  }\n\n  get paused () {\n    return this.#paused\n  }\n}\n\nmodule.exports = class UnwrapHandler {\n  #handler\n  #controller\n\n  constructor (handler) {\n    this.#handler = handler\n  }\n\n  static unwrap (handler) {\n    // TODO (fix): More checks...\n    return !handler.onRequestStart ? handler : new UnwrapHandler(handler)\n  }\n\n  onConnect (abort, context) {\n    this.#controller = new UnwrapController(abort)\n    this.#handler.onRequestStart?.(this.#controller, context)\n  }\n\n  onResponseStarted () {\n    return this.#handler.onResponseStarted?.()\n  }\n\n  onUpgrade (statusCode, rawHeaders, socket) {\n    this.#handler.onRequestUpgrade?.(this.#controller, statusCode, parseHeaders(rawHeaders), socket)\n  }\n\n  onHeaders (statusCode, rawHeaders, resume, statusMessage) {\n    this.#controller[kResume] = resume\n    this.#handler.onResponseStart?.(this.#controller, statusCode, parseHeaders(rawHeaders), statusMessage)\n    return !this.#controller.paused\n  }\n\n  onData (data) {\n    this.#handler.onResponseData?.(this.#controller, data)\n    return !this.#controller.paused\n  }\n\n  onComplete (rawTrailers) {\n    this.#handler.onResponseEnd?.(this.#controller, parseHeaders(rawTrailers))\n  }\n\n  onError (err) {\n    if (!this.#handler.onResponseError) {\n      throw new InvalidArgumentError('invalid onError method')\n    }\n\n    this.#handler.onResponseError?.(this.#controller, err)\n  }\n}\n"
  },
  {
    "path": "lib/handler/wrap-handler.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError } = require('../core/errors')\n\nmodule.exports = class WrapHandler {\n  #handler\n\n  constructor (handler) {\n    this.#handler = handler\n  }\n\n  static wrap (handler) {\n    // TODO (fix): More checks...\n    return handler.onRequestStart ? handler : new WrapHandler(handler)\n  }\n\n  // Unwrap Interface\n\n  onConnect (abort, context) {\n    return this.#handler.onConnect?.(abort, context)\n  }\n\n  onResponseStarted () {\n    return this.#handler.onResponseStarted?.()\n  }\n\n  onHeaders (statusCode, rawHeaders, resume, statusMessage) {\n    return this.#handler.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)\n  }\n\n  onUpgrade (statusCode, rawHeaders, socket) {\n    return this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)\n  }\n\n  onData (data) {\n    return this.#handler.onData?.(data)\n  }\n\n  onComplete (trailers) {\n    return this.#handler.onComplete?.(trailers)\n  }\n\n  onError (err) {\n    if (!this.#handler.onError) {\n      throw err\n    }\n\n    return this.#handler.onError?.(err)\n  }\n\n  // Wrap Interface\n\n  onRequestStart (controller, context) {\n    this.#handler.onConnect?.((reason) => controller.abort(reason), context)\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    const rawHeaders = []\n    for (const [key, val] of Object.entries(headers)) {\n      rawHeaders.push(Buffer.from(key, 'latin1'), toRawHeaderValue(val))\n    }\n\n    this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)\n  }\n\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    const rawHeaders = []\n    for (const [key, val] of Object.entries(headers)) {\n      rawHeaders.push(Buffer.from(key, 'latin1'), toRawHeaderValue(val))\n    }\n\n    if (this.#handler.onHeaders?.(statusCode, rawHeaders, () => controller.resume(), statusMessage) === false) {\n      controller.pause()\n    }\n  }\n\n  onResponseData (controller, data) {\n    if (this.#handler.onData?.(data) === false) {\n      controller.pause()\n    }\n  }\n\n  onResponseEnd (controller, trailers) {\n    const rawTrailers = []\n    for (const [key, val] of Object.entries(trailers)) {\n      rawTrailers.push(Buffer.from(key, 'latin1'), toRawHeaderValue(val))\n    }\n\n    this.#handler.onComplete?.(rawTrailers)\n  }\n\n  onResponseError (controller, err) {\n    if (!this.#handler.onError) {\n      throw new InvalidArgumentError('invalid onError method')\n    }\n\n    this.#handler.onError?.(err)\n  }\n}\n\nfunction toRawHeaderValue (value) {\n  return Array.isArray(value)\n    ? value.map((item) => Buffer.from(item, 'latin1'))\n    : Buffer.from(value, 'latin1')\n}\n"
  },
  {
    "path": "lib/interceptor/cache.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { Readable } = require('node:stream')\nconst util = require('../core/util')\nconst CacheHandler = require('../handler/cache-handler')\nconst MemoryCacheStore = require('../cache/memory-cache-store')\nconst CacheRevalidationHandler = require('../handler/cache-revalidation-handler')\nconst { assertCacheStore, assertCacheMethods, makeCacheKey, normalizeHeaders, parseCacheControlHeader } = require('../util/cache.js')\nconst { AbortError } = require('../core/errors.js')\n\n/**\n * @param {(string | RegExp)[] | undefined} origins\n * @param {string} name\n */\nfunction assertCacheOrigins (origins, name) {\n  if (origins === undefined) return\n  if (!Array.isArray(origins)) {\n    throw new TypeError(`expected ${name} to be an array or undefined, got ${typeof origins}`)\n  }\n  for (let i = 0; i < origins.length; i++) {\n    const origin = origins[i]\n    if (typeof origin !== 'string' && !(origin instanceof RegExp)) {\n      throw new TypeError(`expected ${name}[${i}] to be a string or RegExp, got ${typeof origin}`)\n    }\n  }\n}\n\nconst nop = () => {}\n\n/**\n * @typedef {(options: import('../../types/dispatcher.d.ts').default.DispatchOptions, handler: import('../../types/dispatcher.d.ts').default.DispatchHandler) => void} DispatchFn\n */\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives\n * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts\n * @returns {boolean}\n */\nfunction needsRevalidation (result, cacheControlDirectives, { headers = {} }) {\n  // Always revalidate requests with the no-cache request directive.\n  if (cacheControlDirectives?.['no-cache']) {\n    return true\n  }\n\n  // Always revalidate requests with unqualified no-cache response directive.\n  if (result.cacheControlDirectives?.['no-cache'] && !Array.isArray(result.cacheControlDirectives['no-cache'])) {\n    return true\n  }\n\n  // Always revalidate requests with conditional headers.\n  if (headers['if-modified-since'] || headers['if-none-match']) {\n    return true\n  }\n\n  return false\n}\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives\n * @returns {boolean}\n */\nfunction isStale (result, cacheControlDirectives) {\n  const now = Date.now()\n  if (now > result.staleAt) {\n    // Response is stale\n    if (cacheControlDirectives?.['max-stale']) {\n      // There's a threshold where we can serve stale responses, let's see if\n      //  we're in it\n      // https://www.rfc-editor.org/rfc/rfc9111.html#name-max-stale\n      const gracePeriod = result.staleAt + (cacheControlDirectives['max-stale'] * 1000)\n      return now > gracePeriod\n    }\n\n    return true\n  }\n\n  if (cacheControlDirectives?.['min-fresh']) {\n    // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3\n\n    // At this point, staleAt is always > now\n    const timeLeftTillStale = result.staleAt - now\n    const threshold = cacheControlDirectives['min-fresh'] * 1000\n\n    return timeLeftTillStale <= threshold\n  }\n\n  return false\n}\n\n/**\n * Check if we're within the stale-while-revalidate window for a stale response\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result\n * @returns {boolean}\n */\nfunction withinStaleWhileRevalidateWindow (result) {\n  const staleWhileRevalidate = result.cacheControlDirectives?.['stale-while-revalidate']\n  if (!staleWhileRevalidate) {\n    return false\n  }\n\n  const now = Date.now()\n  const staleWhileRevalidateExpiry = result.staleAt + (staleWhileRevalidate * 1000)\n  return now <= staleWhileRevalidateExpiry\n}\n\n/**\n * @param {DispatchFn} dispatch\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} globalOpts\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey\n * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler\n * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} reqCacheControl\n */\nfunction handleUncachedResponse (\n  dispatch,\n  globalOpts,\n  cacheKey,\n  handler,\n  opts,\n  reqCacheControl\n) {\n  if (reqCacheControl?.['only-if-cached']) {\n    let aborted = false\n    try {\n      if (typeof handler.onConnect === 'function') {\n        handler.onConnect(() => {\n          aborted = true\n        })\n\n        if (aborted) {\n          return\n        }\n      }\n\n      if (typeof handler.onHeaders === 'function') {\n        handler.onHeaders(504, [], nop, 'Gateway Timeout')\n        if (aborted) {\n          return\n        }\n      }\n\n      if (typeof handler.onComplete === 'function') {\n        handler.onComplete([])\n      }\n    } catch (err) {\n      if (typeof handler.onError === 'function') {\n        handler.onError(err)\n      }\n    }\n\n    return true\n  }\n\n  return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))\n}\n\n/**\n * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler\n * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result\n * @param {number} age\n * @param {any} context\n * @param {boolean} isStale\n */\nfunction sendCachedValue (handler, opts, result, age, context, isStale) {\n  // TODO (perf): Readable.from path can be optimized...\n  const stream = util.isStream(result.body)\n    ? result.body\n    : Readable.from(result.body ?? [])\n\n  assert(!stream.destroyed, 'stream should not be destroyed')\n  assert(!stream.readableDidRead, 'stream should not be readableDidRead')\n\n  const controller = {\n    resume () {\n      stream.resume()\n    },\n    pause () {\n      stream.pause()\n    },\n    get paused () {\n      return stream.isPaused()\n    },\n    get aborted () {\n      return stream.destroyed\n    },\n    get reason () {\n      return stream.errored\n    },\n    abort (reason) {\n      stream.destroy(reason ?? new AbortError())\n    }\n  }\n\n  stream\n    .on('error', function (err) {\n      if (!this.readableEnded) {\n        if (typeof handler.onResponseError === 'function') {\n          handler.onResponseError(controller, err)\n        } else {\n          throw err\n        }\n      }\n    })\n    .on('close', function () {\n      if (!this.errored) {\n        handler.onResponseEnd?.(controller, {})\n      }\n    })\n\n  handler.onRequestStart?.(controller, context)\n\n  if (stream.destroyed) {\n    return\n  }\n\n  // Add the age header\n  // https://www.rfc-editor.org/rfc/rfc9111.html#name-age\n  const headers = { ...result.headers, age: String(age) }\n\n  if (isStale) {\n    // Add warning header\n    //  https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Warning\n    headers.warning = '110 - \"response is stale\"'\n  }\n\n  handler.onResponseStart?.(controller, result.statusCode, headers, result.statusMessage)\n\n  if (opts.method === 'HEAD') {\n    stream.destroy()\n  } else {\n    stream.on('data', function (chunk) {\n      handler.onResponseData?.(controller, chunk)\n    })\n  }\n}\n\n/**\n * @param {DispatchFn} dispatch\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} globalOpts\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey\n * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler\n * @param {import('../../types/dispatcher.d.ts').default.RequestOptions} opts\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} reqCacheControl\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined} result\n */\nfunction handleResult (\n  dispatch,\n  globalOpts,\n  cacheKey,\n  handler,\n  opts,\n  reqCacheControl,\n  result\n) {\n  if (!result) {\n    return handleUncachedResponse(dispatch, globalOpts, cacheKey, handler, opts, reqCacheControl)\n  }\n\n  const now = Date.now()\n  if (now > result.deleteAt) {\n    // Response is expired, cache store shouldn't have given this to us\n    return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))\n  }\n\n  const age = Math.round((now - result.cachedAt) / 1000)\n  if (reqCacheControl?.['max-age'] && age >= reqCacheControl['max-age']) {\n    // Response is considered expired for this specific request\n    //  https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1\n    return dispatch(opts, handler)\n  }\n\n  const stale = isStale(result, reqCacheControl)\n  const revalidate = needsRevalidation(result, reqCacheControl, opts)\n\n  // Check if the response is stale\n  if (stale || revalidate) {\n    if (util.isStream(opts.body) && util.bodyLength(opts.body) !== 0) {\n      // If body is a stream we can't revalidate...\n      // TODO (fix): This could be less strict...\n      return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))\n    }\n\n    // RFC 5861: If we're within stale-while-revalidate window, serve stale immediately\n    // and revalidate in background, unless immediate revalidation is necessary\n    if (!revalidate && withinStaleWhileRevalidateWindow(result)) {\n      // Serve stale response immediately\n      sendCachedValue(handler, opts, result, age, null, true)\n\n      // Start background revalidation (fire-and-forget)\n      queueMicrotask(() => {\n        const headers = {\n          ...opts.headers,\n          'if-modified-since': new Date(result.cachedAt).toUTCString()\n        }\n\n        if (result.etag) {\n          headers['if-none-match'] = result.etag\n        }\n\n        if (result.vary) {\n          for (const key in result.vary) {\n            if (result.vary[key] != null) {\n              headers[key] = result.vary[key]\n            }\n          }\n        }\n\n        // Background revalidation - update cache if we get new data\n        dispatch(\n          {\n            ...opts,\n            headers\n          },\n          new CacheHandler(globalOpts, cacheKey, {\n            // Silent handler that just updates the cache\n            onRequestStart () {},\n            onRequestUpgrade () {},\n            onResponseStart () {},\n            onResponseData () {},\n            onResponseEnd () {},\n            onResponseError () {}\n          })\n        )\n      })\n\n      return true\n    }\n\n    let withinStaleIfErrorThreshold = false\n    const staleIfErrorExpiry = result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error']\n    if (staleIfErrorExpiry) {\n      withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000))\n    }\n\n    const headers = {\n      ...opts.headers,\n      'if-modified-since': new Date(result.cachedAt).toUTCString()\n    }\n\n    if (result.etag) {\n      headers['if-none-match'] = result.etag\n    }\n\n    if (result.vary) {\n      for (const key in result.vary) {\n        if (result.vary[key] != null) {\n          headers[key] = result.vary[key]\n        }\n      }\n    }\n\n    // We need to revalidate the response\n    return dispatch(\n      {\n        ...opts,\n        headers\n      },\n      new CacheRevalidationHandler(\n        (success, context) => {\n          if (success) {\n            // TODO: successful revalidation should be considered fresh (not give stale warning).\n            sendCachedValue(handler, opts, result, age, context, stale)\n          } else if (util.isStream(result.body)) {\n            result.body.on('error', nop).destroy()\n          }\n        },\n        new CacheHandler(globalOpts, cacheKey, handler),\n        withinStaleIfErrorThreshold\n      )\n    )\n  }\n\n  // Dump request body.\n  if (util.isStream(opts.body)) {\n    opts.body.on('error', nop).destroy()\n  }\n\n  sendCachedValue(handler, opts, result, age, null, false)\n}\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions} [opts]\n * @returns {import('../../types/dispatcher.d.ts').default.DispatcherComposeInterceptor}\n */\nmodule.exports = (opts = {}) => {\n  const {\n    store = new MemoryCacheStore(),\n    methods = ['GET'],\n    cacheByDefault = undefined,\n    type = 'shared',\n    origins = undefined\n  } = opts\n\n  if (typeof opts !== 'object' || opts === null) {\n    throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? 'null' : typeof opts}`)\n  }\n\n  assertCacheStore(store, 'opts.store')\n  assertCacheMethods(methods, 'opts.methods')\n  assertCacheOrigins(origins, 'opts.origins')\n\n  if (typeof cacheByDefault !== 'undefined' && typeof cacheByDefault !== 'number') {\n    throw new TypeError(`expected opts.cacheByDefault to be number or undefined, got ${typeof cacheByDefault}`)\n  }\n\n  if (typeof type !== 'undefined' && type !== 'shared' && type !== 'private') {\n    throw new TypeError(`expected opts.type to be shared, private, or undefined, got ${typeof type}`)\n  }\n\n  const globalOpts = {\n    store,\n    methods,\n    cacheByDefault,\n    type\n  }\n\n  const safeMethodsToNotCache = util.safeHTTPMethods.filter(method => methods.includes(method) === false)\n\n  return dispatch => {\n    return (opts, handler) => {\n      if (!opts.origin || safeMethodsToNotCache.includes(opts.method)) {\n        // Not a method we want to cache or we don't have the origin, skip\n        return dispatch(opts, handler)\n      }\n\n      // Check if origin is in whitelist\n      if (origins !== undefined) {\n        const requestOrigin = opts.origin.toString().toLowerCase()\n        let isAllowed = false\n\n        for (let i = 0; i < origins.length; i++) {\n          const allowed = origins[i]\n          if (typeof allowed === 'string') {\n            if (allowed.toLowerCase() === requestOrigin) {\n              isAllowed = true\n              break\n            }\n          } else if (allowed.test(requestOrigin)) {\n            isAllowed = true\n            break\n          }\n        }\n\n        if (!isAllowed) {\n          return dispatch(opts, handler)\n        }\n      }\n\n      opts = {\n        ...opts,\n        headers: normalizeHeaders(opts)\n      }\n\n      const reqCacheControl = opts.headers?.['cache-control']\n        ? parseCacheControlHeader(opts.headers['cache-control'])\n        : undefined\n\n      if (reqCacheControl?.['no-store']) {\n        return dispatch(opts, handler)\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const cacheKey = makeCacheKey(opts)\n      const result = store.get(cacheKey)\n\n      if (result && typeof result.then === 'function') {\n        return result\n          .then(result => handleResult(dispatch,\n            globalOpts,\n            cacheKey,\n            handler,\n            opts,\n            reqCacheControl,\n            result\n          ))\n      } else {\n        return handleResult(\n          dispatch,\n          globalOpts,\n          cacheKey,\n          handler,\n          opts,\n          reqCacheControl,\n          result\n        )\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/interceptor/decompress.js",
    "content": "'use strict'\n\nconst { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = require('node:zlib')\nconst { pipeline } = require('node:stream')\nconst DecoratorHandler = require('../handler/decorator-handler')\nconst { runtimeFeatures } = require('../util/runtime-features')\n\n/** @typedef {import('node:stream').Transform} Transform */\n/** @typedef {import('node:stream').Transform} Controller */\n/** @typedef {Transform&import('node:zlib').Zlib} DecompressorStream */\n\n/** @type {Record<string, () => DecompressorStream>} */\nconst supportedEncodings = {\n  gzip: createGunzip,\n  'x-gzip': createGunzip,\n  br: createBrotliDecompress,\n  deflate: createInflate,\n  compress: createInflate,\n  'x-compress': createInflate,\n  ...(runtimeFeatures.has('zstd') ? { zstd: createZstdDecompress } : {})\n}\n\nconst defaultSkipStatusCodes = /** @type {const} */ ([204, 304])\n\nlet warningEmitted = /** @type {boolean} */ (false)\n\n/**\n * @typedef {Object} DecompressHandlerOptions\n * @property {number[]|Readonly<number[]>} [skipStatusCodes=[204, 304]] - List of status codes to skip decompression for\n * @property {boolean} [skipErrorResponses] - Whether to skip decompression for error responses (status codes >= 400)\n */\n\nclass DecompressHandler extends DecoratorHandler {\n  /** @type {Transform[]} */\n  #decompressors = []\n  /** @type {Readonly<number[]>} */\n  #skipStatusCodes\n  /** @type {boolean} */\n  #skipErrorResponses\n\n  constructor (handler, { skipStatusCodes = defaultSkipStatusCodes, skipErrorResponses = true } = {}) {\n    super(handler)\n    this.#skipStatusCodes = skipStatusCodes\n    this.#skipErrorResponses = skipErrorResponses\n  }\n\n  /**\n   * Determines if decompression should be skipped based on encoding and status code\n   * @param {string} contentEncoding - Content-Encoding header value\n   * @param {number} statusCode - HTTP status code of the response\n   * @returns {boolean} - True if decompression should be skipped\n   */\n  #shouldSkipDecompression (contentEncoding, statusCode) {\n    if (!contentEncoding || statusCode < 200) return true\n    if (this.#skipStatusCodes.includes(statusCode)) return true\n    if (this.#skipErrorResponses && statusCode >= 400) return true\n    return false\n  }\n\n  /**\n   * Creates a chain of decompressors for multiple content encodings\n   *\n   * @param {string} encodings - Comma-separated list of content encodings\n   * @returns {Array<DecompressorStream>} - Array of decompressor streams\n   * @throws {Error} - If the number of content-encodings exceeds the maximum allowed\n   */\n  #createDecompressionChain (encodings) {\n    const parts = encodings.split(',')\n\n    // Limit the number of content-encodings to prevent resource exhaustion.\n    // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).\n    const maxContentEncodings = 5\n    if (parts.length > maxContentEncodings) {\n      throw new Error(`too many content-encodings in response: ${parts.length}, maximum allowed is ${maxContentEncodings}`)\n    }\n\n    /** @type {DecompressorStream[]} */\n    const decompressors = []\n\n    for (let i = parts.length - 1; i >= 0; i--) {\n      const encoding = parts[i].trim()\n      if (!encoding) continue\n\n      if (!supportedEncodings[encoding]) {\n        decompressors.length = 0 // Clear if unsupported encoding\n        return decompressors // Unsupported encoding\n      }\n\n      decompressors.push(supportedEncodings[encoding]())\n    }\n\n    return decompressors\n  }\n\n  /**\n   * Sets up event handlers for a decompressor stream using readable events\n   * @param {DecompressorStream} decompressor - The decompressor stream\n   * @param {Controller} controller - The controller to coordinate with\n   * @returns {void}\n   */\n  #setupDecompressorEvents (decompressor, controller) {\n    decompressor.on('readable', () => {\n      let chunk\n      while ((chunk = decompressor.read()) !== null) {\n        const result = super.onResponseData(controller, chunk)\n        if (result === false) {\n          break\n        }\n      }\n    })\n\n    decompressor.on('error', (error) => {\n      super.onResponseError(controller, error)\n    })\n  }\n\n  /**\n   * Sets up event handling for a single decompressor\n   * @param {Controller} controller - The controller to handle events\n   * @returns {void}\n   */\n  #setupSingleDecompressor (controller) {\n    const decompressor = this.#decompressors[0]\n    this.#setupDecompressorEvents(decompressor, controller)\n\n    decompressor.on('end', () => {\n      super.onResponseEnd(controller, {})\n    })\n  }\n\n  /**\n   * Sets up event handling for multiple chained decompressors using pipeline\n   * @param {Controller} controller - The controller to handle events\n   * @returns {void}\n   */\n  #setupMultipleDecompressors (controller) {\n    const lastDecompressor = this.#decompressors[this.#decompressors.length - 1]\n    this.#setupDecompressorEvents(lastDecompressor, controller)\n\n    pipeline(this.#decompressors, (err) => {\n      if (err) {\n        super.onResponseError(controller, err)\n        return\n      }\n      super.onResponseEnd(controller, {})\n    })\n  }\n\n  /**\n   * Cleans up decompressor references to prevent memory leaks\n   * @returns {void}\n   */\n  #cleanupDecompressors () {\n    this.#decompressors.length = 0\n  }\n\n  /**\n   * @param {Controller} controller\n   * @param {number} statusCode\n   * @param {Record<string, string | string[] | undefined>} headers\n   * @param {string} statusMessage\n   * @returns {void}\n   */\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    const contentEncoding = headers['content-encoding']\n\n    // If content encoding is not supported or status code is in skip list\n    if (this.#shouldSkipDecompression(contentEncoding, statusCode)) {\n      return super.onResponseStart(controller, statusCode, headers, statusMessage)\n    }\n\n    const decompressors = this.#createDecompressionChain(contentEncoding.toLowerCase())\n\n    if (decompressors.length === 0) {\n      this.#cleanupDecompressors()\n      return super.onResponseStart(controller, statusCode, headers, statusMessage)\n    }\n\n    this.#decompressors = decompressors\n\n    // Remove compression headers since we're decompressing\n    const { 'content-encoding': _, 'content-length': __, ...newHeaders } = headers\n\n    if (this.#decompressors.length === 1) {\n      this.#setupSingleDecompressor(controller)\n    } else {\n      this.#setupMultipleDecompressors(controller)\n    }\n\n    return super.onResponseStart(controller, statusCode, newHeaders, statusMessage)\n  }\n\n  /**\n   * @param {Controller} controller\n   * @param {Buffer} chunk\n   * @returns {void}\n   */\n  onResponseData (controller, chunk) {\n    if (this.#decompressors.length > 0) {\n      this.#decompressors[0].write(chunk)\n      return\n    }\n    super.onResponseData(controller, chunk)\n  }\n\n  /**\n   * @param {Controller} controller\n   * @param {Record<string, string | string[]> | undefined} trailers\n   * @returns {void}\n   */\n  onResponseEnd (controller, trailers) {\n    if (this.#decompressors.length > 0) {\n      this.#decompressors[0].end()\n      this.#cleanupDecompressors()\n      return\n    }\n    super.onResponseEnd(controller, trailers)\n  }\n\n  /**\n   * @param {Controller} controller\n   * @param {Error} err\n   * @returns {void}\n   */\n  onResponseError (controller, err) {\n    if (this.#decompressors.length > 0) {\n      for (const decompressor of this.#decompressors) {\n        decompressor.destroy(err)\n      }\n      this.#cleanupDecompressors()\n    }\n    super.onResponseError(controller, err)\n  }\n}\n\n/**\n * Creates a decompression interceptor for HTTP responses\n * @param {DecompressHandlerOptions} [options] - Options for the interceptor\n * @returns {Function} - Interceptor function\n */\nfunction createDecompressInterceptor (options = {}) {\n  // Emit experimental warning only once\n  if (!warningEmitted) {\n    process.emitWarning(\n      'DecompressInterceptor is experimental and subject to change',\n      'ExperimentalWarning'\n    )\n    warningEmitted = true\n  }\n\n  return (dispatch) => {\n    return (opts, handler) => {\n      const decompressHandler = new DecompressHandler(handler, options)\n      return dispatch(opts, decompressHandler)\n    }\n  }\n}\n\nmodule.exports = createDecompressInterceptor\n"
  },
  {
    "path": "lib/interceptor/deduplicate.js",
    "content": "'use strict'\n\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst util = require('../core/util')\nconst DeduplicationHandler = require('../handler/deduplication-handler')\nconst { normalizeHeaders, makeCacheKey, makeDeduplicationKey } = require('../util/cache.js')\n\nconst pendingRequestsChannel = diagnosticsChannel.channel('undici:request:pending-requests')\n\n/**\n * @param {import('../../types/interceptors.d.ts').default.DeduplicateInterceptorOpts} [opts]\n * @returns {import('../../types/dispatcher.d.ts').default.DispatcherComposeInterceptor}\n */\nmodule.exports = (opts = {}) => {\n  const {\n    methods = ['GET'],\n    skipHeaderNames = [],\n    excludeHeaderNames = [],\n    maxBufferSize = 5 * 1024 * 1024\n  } = opts\n\n  if (typeof opts !== 'object' || opts === null) {\n    throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? 'null' : typeof opts}`)\n  }\n\n  if (!Array.isArray(methods)) {\n    throw new TypeError(`expected opts.methods to be an array, got ${typeof methods}`)\n  }\n\n  for (const method of methods) {\n    if (!util.safeHTTPMethods.includes(method)) {\n      throw new TypeError(`expected opts.methods to only contain safe HTTP methods, got ${method}`)\n    }\n  }\n\n  if (!Array.isArray(skipHeaderNames)) {\n    throw new TypeError(`expected opts.skipHeaderNames to be an array, got ${typeof skipHeaderNames}`)\n  }\n\n  if (!Array.isArray(excludeHeaderNames)) {\n    throw new TypeError(`expected opts.excludeHeaderNames to be an array, got ${typeof excludeHeaderNames}`)\n  }\n\n  if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n    throw new TypeError(`expected opts.maxBufferSize to be a positive finite number, got ${maxBufferSize}`)\n  }\n\n  // Convert to lowercase Set for case-insensitive header matching\n  const skipHeaderNamesSet = new Set(skipHeaderNames.map(name => name.toLowerCase()))\n\n  // Convert to lowercase Set for case-insensitive header exclusion from deduplication key\n  const excludeHeaderNamesSet = new Set(excludeHeaderNames.map(name => name.toLowerCase()))\n\n  /**\n   * Map of pending requests for deduplication\n   * @type {Map<string, DeduplicationHandler>}\n   */\n  const pendingRequests = new Map()\n\n  return dispatch => {\n    return (opts, handler) => {\n      if (!opts.origin || methods.includes(opts.method) === false) {\n        return dispatch(opts, handler)\n      }\n\n      opts = {\n        ...opts,\n        headers: normalizeHeaders(opts)\n      }\n\n      // Skip deduplication if request contains any of the specified headers\n      if (skipHeaderNamesSet.size > 0) {\n        for (const headerName of Object.keys(opts.headers)) {\n          if (skipHeaderNamesSet.has(headerName.toLowerCase())) {\n            return dispatch(opts, handler)\n          }\n        }\n      }\n\n      const cacheKey = makeCacheKey(opts)\n      const dedupeKey = makeDeduplicationKey(cacheKey, excludeHeaderNamesSet)\n\n      // Check if there's already a pending request for this key\n      const pendingHandler = pendingRequests.get(dedupeKey)\n      if (pendingHandler) {\n        // Add this handler to the waiting list when safe.\n        // If body streaming has already started, this request must be sent independently.\n        if (pendingHandler.addWaitingHandler(handler)) {\n          return true\n        }\n\n        return dispatch(opts, handler)\n      }\n\n      // Create a new deduplication handler\n      const deduplicationHandler = new DeduplicationHandler(\n        handler,\n        () => {\n          // Clean up when request completes\n          pendingRequests.delete(dedupeKey)\n          if (pendingRequestsChannel.hasSubscribers) {\n            pendingRequestsChannel.publish({ size: pendingRequests.size, key: dedupeKey, type: 'removed' })\n          }\n        },\n        maxBufferSize\n      )\n\n      // Register the pending request\n      pendingRequests.set(dedupeKey, deduplicationHandler)\n      if (pendingRequestsChannel.hasSubscribers) {\n        pendingRequestsChannel.publish({ size: pendingRequests.size, key: dedupeKey, type: 'added' })\n      }\n\n      return dispatch(opts, deduplicationHandler)\n    }\n  }\n}\n"
  },
  {
    "path": "lib/interceptor/dns.js",
    "content": "'use strict'\nconst { isIP } = require('node:net')\nconst { lookup } = require('node:dns')\nconst DecoratorHandler = require('../handler/decorator-handler')\nconst { InvalidArgumentError, InformationalError } = require('../core/errors')\nconst maxInt = Math.pow(2, 31) - 1\n\nfunction hasSafeIterator (headers) {\n  const prototype = Object.getPrototypeOf(headers)\n  const ownIterator = Object.prototype.hasOwnProperty.call(headers, Symbol.iterator)\n  return ownIterator || (prototype != null && prototype !== Object.prototype && typeof headers[Symbol.iterator] === 'function')\n}\n\nfunction isHostHeader (key) {\n  return typeof key === 'string' && key.toLowerCase() === 'host'\n}\n\nfunction normalizeHeaders (headers) {\n  if (headers == null) {\n    return null\n  }\n\n  if (Array.isArray(headers)) {\n    if (headers.length === 0 || !Array.isArray(headers[0])) {\n      return headers\n    }\n\n    const normalized = []\n    for (const header of headers) {\n      if (Array.isArray(header) && header.length === 2) {\n        normalized.push(header[0], header[1])\n      } else {\n        normalized.push(header)\n      }\n    }\n\n    return normalized\n  }\n\n  if (typeof headers === 'object' && hasSafeIterator(headers)) {\n    const normalized = []\n    for (const header of headers) {\n      if (Array.isArray(header) && header.length === 2) {\n        normalized.push(header[0], header[1])\n      } else {\n        normalized.push(header)\n      }\n    }\n\n    return normalized\n  }\n\n  return headers\n}\n\nfunction hasHostHeader (headers) {\n  if (headers == null) {\n    return false\n  }\n\n  if (Array.isArray(headers)) {\n    if (headers.length === 0) {\n      return false\n    }\n\n    for (let i = 0; i < headers.length; i += 2) {\n      if (isHostHeader(headers[i])) {\n        return true\n      }\n    }\n\n    return false\n  }\n\n  if (typeof headers === 'object') {\n    for (const key in headers) {\n      if (isHostHeader(key)) {\n        return true\n      }\n    }\n  }\n\n  return false\n}\n\nfunction withHostHeader (host, headers) {\n  const normalizedHeaders = normalizeHeaders(headers)\n\n  if (hasHostHeader(normalizedHeaders)) {\n    return normalizedHeaders\n  }\n\n  if (Array.isArray(normalizedHeaders)) {\n    return ['host', host, ...normalizedHeaders]\n  }\n\n  if (normalizedHeaders && typeof normalizedHeaders === 'object') {\n    return {\n      host,\n      ...normalizedHeaders\n    }\n  }\n\n  return { host }\n}\n\nclass DNSStorage {\n  #maxItems = 0\n  #records = new Map()\n\n  constructor (opts) {\n    this.#maxItems = opts.maxItems\n  }\n\n  get size () {\n    return this.#records.size\n  }\n\n  get (hostname) {\n    return this.#records.get(hostname) ?? null\n  }\n\n  set (hostname, records) {\n    this.#records.set(hostname, records)\n  }\n\n  delete (hostname) {\n    this.#records.delete(hostname)\n  }\n\n  // Delegate to storage decide can we do more lookups or not\n  full () {\n    return this.size >= this.#maxItems\n  }\n}\n\nclass DNSInstance {\n  #maxTTL = 0\n  #maxItems = 0\n  dualStack = true\n  affinity = null\n  lookup = null\n  pick = null\n  storage = null\n\n  constructor (opts) {\n    this.#maxTTL = opts.maxTTL\n    this.#maxItems = opts.maxItems\n    this.dualStack = opts.dualStack\n    this.affinity = opts.affinity\n    this.lookup = opts.lookup ?? this.#defaultLookup\n    this.pick = opts.pick ?? this.#defaultPick\n    this.storage = opts.storage ?? new DNSStorage(opts)\n  }\n\n  runLookup (origin, opts, cb) {\n    const ips = this.storage.get(origin.hostname)\n\n    // If full, we just return the origin\n    if (ips == null && this.storage.full()) {\n      cb(null, origin)\n      return\n    }\n\n    const newOpts = {\n      affinity: this.affinity,\n      dualStack: this.dualStack,\n      lookup: this.lookup,\n      pick: this.pick,\n      ...opts.dns,\n      maxTTL: this.#maxTTL,\n      maxItems: this.#maxItems\n    }\n\n    // If no IPs we lookup\n    if (ips == null) {\n      this.lookup(origin, newOpts, (err, addresses) => {\n        if (err || addresses == null || addresses.length === 0) {\n          cb(err ?? new InformationalError('No DNS entries found'))\n          return\n        }\n\n        this.setRecords(origin, addresses)\n        const records = this.storage.get(origin.hostname)\n\n        const ip = this.pick(\n          origin,\n          records,\n          newOpts.affinity\n        )\n\n        let port\n        if (typeof ip.port === 'number') {\n          port = `:${ip.port}`\n        } else if (origin.port !== '') {\n          port = `:${origin.port}`\n        } else {\n          port = ''\n        }\n\n        cb(\n          null,\n          new URL(`${origin.protocol}//${\n            ip.family === 6 ? `[${ip.address}]` : ip.address\n          }${port}`)\n        )\n      })\n    } else {\n      // If there's IPs we pick\n      const ip = this.pick(\n        origin,\n        ips,\n        newOpts.affinity\n      )\n\n      // If no IPs we lookup - deleting old records\n      if (ip == null) {\n        this.storage.delete(origin.hostname)\n        this.runLookup(origin, opts, cb)\n        return\n      }\n\n      let port\n      if (typeof ip.port === 'number') {\n        port = `:${ip.port}`\n      } else if (origin.port !== '') {\n        port = `:${origin.port}`\n      } else {\n        port = ''\n      }\n\n      cb(\n        null,\n        new URL(`${origin.protocol}//${\n          ip.family === 6 ? `[${ip.address}]` : ip.address\n        }${port}`)\n      )\n    }\n  }\n\n  #defaultLookup (origin, opts, cb) {\n    lookup(\n      origin.hostname,\n      {\n        all: true,\n        family: this.dualStack === false ? this.affinity : 0,\n        order: 'ipv4first'\n      },\n      (err, addresses) => {\n        if (err) {\n          return cb(err)\n        }\n\n        const results = new Map()\n\n        for (const addr of addresses) {\n          // On linux we found duplicates, we attempt to remove them with\n          // the latest record\n          results.set(`${addr.address}:${addr.family}`, addr)\n        }\n\n        cb(null, results.values())\n      }\n    )\n  }\n\n  #defaultPick (origin, hostnameRecords, affinity) {\n    let ip = null\n    const { records, offset } = hostnameRecords\n\n    let family\n    if (this.dualStack) {\n      if (affinity == null) {\n        // Balance between ip families\n        if (offset == null || offset === maxInt) {\n          hostnameRecords.offset = 0\n          affinity = 4\n        } else {\n          hostnameRecords.offset++\n          affinity = (hostnameRecords.offset & 1) === 1 ? 6 : 4\n        }\n      }\n\n      if (records[affinity] != null && records[affinity].ips.length > 0) {\n        family = records[affinity]\n      } else {\n        family = records[affinity === 4 ? 6 : 4]\n      }\n    } else {\n      family = records[affinity]\n    }\n\n    // If no IPs we return null\n    if (family == null || family.ips.length === 0) {\n      return ip\n    }\n\n    if (family.offset == null || family.offset === maxInt) {\n      family.offset = 0\n    } else {\n      family.offset++\n    }\n\n    const position = family.offset % family.ips.length\n    ip = family.ips[position] ?? null\n\n    if (ip == null) {\n      return ip\n    }\n\n    if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms\n      // We delete expired records\n      // It is possible that they have different TTL, so we manage them individually\n      family.ips.splice(position, 1)\n      return this.pick(origin, hostnameRecords, affinity)\n    }\n\n    return ip\n  }\n\n  pickFamily (origin, ipFamily) {\n    const records = this.storage.get(origin.hostname)?.records\n    if (!records) {\n      return null\n    }\n\n    const family = records[ipFamily]\n    if (!family) {\n      return null\n    }\n\n    if (family.offset == null || family.offset === maxInt) {\n      family.offset = 0\n    } else {\n      family.offset++\n    }\n\n    const position = family.offset % family.ips.length\n    const ip = family.ips[position] ?? null\n    if (ip == null) {\n      return ip\n    }\n\n    if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms\n      // We delete expired records\n      // It is possible that they have different TTL, so we manage them individually\n      family.ips.splice(position, 1)\n    }\n\n    return ip\n  }\n\n  setRecords (origin, addresses) {\n    const timestamp = Date.now()\n    const records = { records: { 4: null, 6: null } }\n    let minTTL = this.#maxTTL\n    for (const record of addresses) {\n      record.timestamp = timestamp\n      if (typeof record.ttl === 'number') {\n        // The record TTL is expected to be in ms\n        record.ttl = Math.min(record.ttl, this.#maxTTL)\n        minTTL = Math.min(minTTL, record.ttl)\n      } else {\n        record.ttl = this.#maxTTL\n      }\n\n      const familyRecords = records.records[record.family] ?? { ips: [] }\n\n      familyRecords.ips.push(record)\n      records.records[record.family] = familyRecords\n    }\n\n    // We provide a default TTL if external storage will be used without TTL per record-level support\n    this.storage.set(origin.hostname, records, { ttl: minTTL })\n  }\n\n  deleteRecords (origin) {\n    this.storage.delete(origin.hostname)\n  }\n\n  getHandler (meta, opts) {\n    return new DNSDispatchHandler(this, meta, opts)\n  }\n}\n\nclass DNSDispatchHandler extends DecoratorHandler {\n  #state = null\n  #opts = null\n  #dispatch = null\n  #origin = null\n  #controller = null\n  #newOrigin = null\n  #firstTry = true\n\n  constructor (state, { origin, handler, dispatch, newOrigin }, opts) {\n    super(handler)\n    this.#origin = origin\n    this.#newOrigin = newOrigin\n    this.#opts = { ...opts }\n    this.#state = state\n    this.#dispatch = dispatch\n  }\n\n  onResponseError (controller, err) {\n    switch (err.code) {\n      case 'ETIMEDOUT':\n      case 'ECONNREFUSED': {\n        if (this.#state.dualStack) {\n          if (!this.#firstTry) {\n            super.onResponseError(controller, err)\n            return\n          }\n          this.#firstTry = false\n\n          // Pick an ip address from the other family\n          const otherFamily = this.#newOrigin.hostname[0] === '[' ? 4 : 6\n          const ip = this.#state.pickFamily(this.#origin, otherFamily)\n          if (ip == null) {\n            super.onResponseError(controller, err)\n            return\n          }\n\n          let port\n          if (typeof ip.port === 'number') {\n            port = `:${ip.port}`\n          } else if (this.#origin.port !== '') {\n            port = `:${this.#origin.port}`\n          } else {\n            port = ''\n          }\n\n          const dispatchOpts = {\n            ...this.#opts,\n            origin: `${this.#origin.protocol}//${\n              ip.family === 6 ? `[${ip.address}]` : ip.address\n            }${port}`,\n            headers: withHostHeader(this.#origin.host, this.#opts.headers)\n          }\n          this.#dispatch(dispatchOpts, this)\n          return\n        }\n\n        // if dual-stack disabled, we error out\n        super.onResponseError(controller, err)\n        break\n      }\n      case 'ENOTFOUND':\n        this.#state.deleteRecords(this.#origin)\n        super.onResponseError(controller, err)\n        break\n      default:\n        super.onResponseError(controller, err)\n        break\n    }\n  }\n}\n\nmodule.exports = interceptorOpts => {\n  if (\n    interceptorOpts?.maxTTL != null &&\n    (typeof interceptorOpts?.maxTTL !== 'number' || interceptorOpts?.maxTTL < 0)\n  ) {\n    throw new InvalidArgumentError('Invalid maxTTL. Must be a positive number')\n  }\n\n  if (\n    interceptorOpts?.maxItems != null &&\n    (typeof interceptorOpts?.maxItems !== 'number' ||\n      interceptorOpts?.maxItems < 1)\n  ) {\n    throw new InvalidArgumentError(\n      'Invalid maxItems. Must be a positive number and greater than zero'\n    )\n  }\n\n  if (\n    interceptorOpts?.affinity != null &&\n    interceptorOpts?.affinity !== 4 &&\n    interceptorOpts?.affinity !== 6\n  ) {\n    throw new InvalidArgumentError('Invalid affinity. Must be either 4 or 6')\n  }\n\n  if (\n    interceptorOpts?.dualStack != null &&\n    typeof interceptorOpts?.dualStack !== 'boolean'\n  ) {\n    throw new InvalidArgumentError('Invalid dualStack. Must be a boolean')\n  }\n\n  if (\n    interceptorOpts?.lookup != null &&\n    typeof interceptorOpts?.lookup !== 'function'\n  ) {\n    throw new InvalidArgumentError('Invalid lookup. Must be a function')\n  }\n\n  if (\n    interceptorOpts?.pick != null &&\n    typeof interceptorOpts?.pick !== 'function'\n  ) {\n    throw new InvalidArgumentError('Invalid pick. Must be a function')\n  }\n\n  if (\n    interceptorOpts?.storage != null &&\n    (typeof interceptorOpts?.storage?.get !== 'function' ||\n      typeof interceptorOpts?.storage?.set !== 'function' ||\n      typeof interceptorOpts?.storage?.full !== 'function' ||\n      typeof interceptorOpts?.storage?.delete !== 'function'\n    )\n  ) {\n    throw new InvalidArgumentError('Invalid storage. Must be a object with methods: { get, set, full, delete }')\n  }\n\n  const dualStack = interceptorOpts?.dualStack ?? true\n  let affinity\n  if (dualStack) {\n    affinity = interceptorOpts?.affinity ?? null\n  } else {\n    affinity = interceptorOpts?.affinity ?? 4\n  }\n\n  const opts = {\n    maxTTL: interceptorOpts?.maxTTL ?? 10e3, // Expressed in ms\n    lookup: interceptorOpts?.lookup ?? null,\n    pick: interceptorOpts?.pick ?? null,\n    dualStack,\n    affinity,\n    maxItems: interceptorOpts?.maxItems ?? Infinity,\n    storage: interceptorOpts?.storage\n  }\n\n  const instance = new DNSInstance(opts)\n\n  return dispatch => {\n    return function dnsInterceptor (origDispatchOpts, handler) {\n      const origin =\n        origDispatchOpts.origin.constructor === URL\n          ? origDispatchOpts.origin\n          : new URL(origDispatchOpts.origin)\n\n      if (isIP(origin.hostname) !== 0) {\n        return dispatch(origDispatchOpts, handler)\n      }\n\n      instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {\n        if (err) {\n          return handler.onResponseError(null, err)\n        }\n\n        const dispatchOpts = {\n          ...origDispatchOpts,\n          servername: origin.hostname, // For SNI on TLS\n          origin: newOrigin.origin,\n          headers: withHostHeader(origin.host, origDispatchOpts.headers)\n        }\n\n        dispatch(\n          dispatchOpts,\n          instance.getHandler(\n            { origin, dispatch, handler, newOrigin },\n            origDispatchOpts\n          )\n        )\n      })\n\n      return true\n    }\n  }\n}\n"
  },
  {
    "path": "lib/interceptor/dump.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError, RequestAbortedError } = require('../core/errors')\nconst DecoratorHandler = require('../handler/decorator-handler')\n\nclass DumpHandler extends DecoratorHandler {\n  #maxSize = 1024 * 1024\n  #dumped = false\n  #size = 0\n  #controller = null\n  aborted = false\n  reason = false\n\n  constructor ({ maxSize, signal }, handler) {\n    if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {\n      throw new InvalidArgumentError('maxSize must be a number greater than 0')\n    }\n\n    super(handler)\n\n    this.#maxSize = maxSize ?? this.#maxSize\n    // this.#handler = handler\n  }\n\n  #abort (reason) {\n    this.aborted = true\n    this.reason = reason\n  }\n\n  onRequestStart (controller, context) {\n    controller.abort = this.#abort.bind(this)\n    this.#controller = controller\n\n    return super.onRequestStart(controller, context)\n  }\n\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    const contentLength = headers['content-length']\n\n    if (contentLength != null && contentLength > this.#maxSize) {\n      throw new RequestAbortedError(\n        `Response size (${contentLength}) larger than maxSize (${\n          this.#maxSize\n        })`\n      )\n    }\n\n    if (this.aborted === true) {\n      return true\n    }\n\n    return super.onResponseStart(controller, statusCode, headers, statusMessage)\n  }\n\n  onResponseError (controller, err) {\n    if (this.#dumped) {\n      return\n    }\n\n    // On network errors before connect, controller will be null\n    err = this.#controller?.reason ?? err\n\n    super.onResponseError(controller, err)\n  }\n\n  onResponseData (controller, chunk) {\n    this.#size = this.#size + chunk.length\n\n    if (this.#size >= this.#maxSize) {\n      this.#dumped = true\n\n      if (this.aborted === true) {\n        super.onResponseError(controller, this.reason)\n      } else {\n        super.onResponseEnd(controller, {})\n      }\n    }\n\n    return true\n  }\n\n  onResponseEnd (controller, trailers) {\n    if (this.#dumped) {\n      return\n    }\n\n    if (this.#controller.aborted === true) {\n      super.onResponseError(controller, this.reason)\n      return\n    }\n\n    super.onResponseEnd(controller, trailers)\n  }\n}\n\nfunction createDumpInterceptor (\n  { maxSize: defaultMaxSize } = {\n    maxSize: 1024 * 1024\n  }\n) {\n  return dispatch => {\n    return function Intercept (opts, handler) {\n      const { dumpMaxSize = defaultMaxSize } = opts\n\n      const dumpHandler = new DumpHandler({ maxSize: dumpMaxSize, signal: opts.signal }, handler)\n\n      return dispatch(opts, dumpHandler)\n    }\n  }\n}\n\nmodule.exports = createDumpInterceptor\n"
  },
  {
    "path": "lib/interceptor/redirect.js",
    "content": "'use strict'\n\nconst RedirectHandler = require('../handler/redirect-handler')\n\nfunction createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections } = {}) {\n  return (dispatch) => {\n    return function Intercept (opts, handler) {\n      const { maxRedirections = defaultMaxRedirections, ...rest } = opts\n\n      if (maxRedirections == null || maxRedirections === 0) {\n        return dispatch(opts, handler)\n      }\n\n      const dispatchOpts = { ...rest } // Stop sub dispatcher from also redirecting.\n      const redirectHandler = new RedirectHandler(dispatch, maxRedirections, dispatchOpts, handler)\n      return dispatch(dispatchOpts, redirectHandler)\n    }\n  }\n}\n\nmodule.exports = createRedirectInterceptor\n"
  },
  {
    "path": "lib/interceptor/response-error.js",
    "content": "'use strict'\n\n// const { parseHeaders } = require('../core/util')\nconst DecoratorHandler = require('../handler/decorator-handler')\nconst { ResponseError } = require('../core/errors')\n\nclass ResponseErrorHandler extends DecoratorHandler {\n  #statusCode\n  #contentType\n  #decoder\n  #headers\n  #body\n\n  constructor (_opts, { handler }) {\n    super(handler)\n  }\n\n  #checkContentType (contentType) {\n    return (this.#contentType ?? '').indexOf(contentType) === 0\n  }\n\n  onRequestStart (controller, context) {\n    this.#statusCode = 0\n    this.#contentType = null\n    this.#decoder = null\n    this.#headers = null\n    this.#body = ''\n\n    return super.onRequestStart(controller, context)\n  }\n\n  onResponseStart (controller, statusCode, headers, statusMessage) {\n    this.#statusCode = statusCode\n    this.#headers = headers\n    this.#contentType = headers['content-type']\n\n    if (this.#statusCode < 400) {\n      return super.onResponseStart(controller, statusCode, headers, statusMessage)\n    }\n\n    if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) {\n      this.#decoder = new TextDecoder('utf-8')\n    }\n  }\n\n  onResponseData (controller, chunk) {\n    if (this.#statusCode < 400) {\n      return super.onResponseData(controller, chunk)\n    }\n\n    this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''\n  }\n\n  onResponseEnd (controller, trailers) {\n    if (this.#statusCode >= 400) {\n      this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''\n\n      if (this.#checkContentType('application/json')) {\n        try {\n          this.#body = JSON.parse(this.#body)\n        } catch {\n          // Do nothing...\n        }\n      }\n\n      let err\n      const stackTraceLimit = Error.stackTraceLimit\n      Error.stackTraceLimit = 0\n      try {\n        err = new ResponseError('Response Error', this.#statusCode, {\n          body: this.#body,\n          headers: this.#headers\n        })\n      } finally {\n        Error.stackTraceLimit = stackTraceLimit\n      }\n\n      super.onResponseError(controller, err)\n    } else {\n      super.onResponseEnd(controller, trailers)\n    }\n  }\n\n  onResponseError (controller, err) {\n    super.onResponseError(controller, err)\n  }\n}\n\nmodule.exports = () => {\n  return (dispatch) => {\n    return function Intercept (opts, handler) {\n      return dispatch(opts, new ResponseErrorHandler(opts, { handler }))\n    }\n  }\n}\n"
  },
  {
    "path": "lib/interceptor/retry.js",
    "content": "'use strict'\nconst RetryHandler = require('../handler/retry-handler')\n\nmodule.exports = globalOpts => {\n  return dispatch => {\n    return function retryInterceptor (opts, handler) {\n      return dispatch(\n        opts,\n        new RetryHandler(\n          { ...opts, retryOptions: { ...globalOpts, ...opts.retryOptions } },\n          {\n            handler,\n            dispatch\n          }\n        )\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "lib/llhttp/.gitkeep",
    "content": ""
  },
  {
    "path": "lib/llhttp/constants.d.ts",
    "content": "export type IntDict = Record<string, number>;\nexport declare const ERROR: IntDict;\nexport declare const TYPE: IntDict;\nexport declare const FLAGS: IntDict;\nexport declare const LENIENT_FLAGS: IntDict;\nexport declare const METHODS: IntDict;\nexport declare const STATUSES: IntDict;\nexport declare const FINISH: IntDict;\nexport declare const HEADER_STATE: IntDict;\nexport declare const METHODS_HTTP: number[];\nexport declare const METHODS_ICE: number[];\nexport declare const METHODS_RTSP: number[];\nexport declare const METHOD_MAP: IntDict;\nexport declare const H_METHOD_MAP: {\n    [k: string]: number;\n};\nexport declare const STATUSES_HTTP: number[];\nexport type CharList = (string | number)[];\nexport declare const ALPHA: CharList;\nexport declare const NUM_MAP: {\n    0: number;\n    1: number;\n    2: number;\n    3: number;\n    4: number;\n    5: number;\n    6: number;\n    7: number;\n    8: number;\n    9: number;\n};\nexport declare const HEX_MAP: {\n    0: number;\n    1: number;\n    2: number;\n    3: number;\n    4: number;\n    5: number;\n    6: number;\n    7: number;\n    8: number;\n    9: number;\n    A: number;\n    B: number;\n    C: number;\n    D: number;\n    E: number;\n    F: number;\n    a: number;\n    b: number;\n    c: number;\n    d: number;\n    e: number;\n    f: number;\n};\nexport declare const NUM: CharList;\nexport declare const ALPHANUM: CharList;\nexport declare const MARK: CharList;\nexport declare const USERINFO_CHARS: CharList;\nexport declare const URL_CHAR: CharList;\nexport declare const HEX: CharList;\nexport declare const TOKEN: CharList;\nexport declare const HEADER_CHARS: CharList;\nexport declare const CONNECTION_TOKEN_CHARS: CharList;\nexport declare const QUOTED_STRING: CharList;\nexport declare const HTAB_SP_VCHAR_OBS_TEXT: CharList;\nexport declare const MAJOR: {\n    0: number;\n    1: number;\n    2: number;\n    3: number;\n    4: number;\n    5: number;\n    6: number;\n    7: number;\n    8: number;\n    9: number;\n};\nexport declare const MINOR: {\n    0: number;\n    1: number;\n    2: number;\n    3: number;\n    4: number;\n    5: number;\n    6: number;\n    7: number;\n    8: number;\n    9: number;\n};\nexport declare const SPECIAL_HEADERS: {\n    connection: number;\n    'content-length': number;\n    'proxy-connection': number;\n    'transfer-encoding': number;\n    upgrade: number;\n};\ndeclare const _default: {\n    ERROR: IntDict;\n    TYPE: IntDict;\n    FLAGS: IntDict;\n    LENIENT_FLAGS: IntDict;\n    METHODS: IntDict;\n    STATUSES: IntDict;\n    FINISH: IntDict;\n    HEADER_STATE: IntDict;\n    ALPHA: CharList;\n    NUM_MAP: {\n        0: number;\n        1: number;\n        2: number;\n        3: number;\n        4: number;\n        5: number;\n        6: number;\n        7: number;\n        8: number;\n        9: number;\n    };\n    HEX_MAP: {\n        0: number;\n        1: number;\n        2: number;\n        3: number;\n        4: number;\n        5: number;\n        6: number;\n        7: number;\n        8: number;\n        9: number;\n        A: number;\n        B: number;\n        C: number;\n        D: number;\n        E: number;\n        F: number;\n        a: number;\n        b: number;\n        c: number;\n        d: number;\n        e: number;\n        f: number;\n    };\n    NUM: CharList;\n    ALPHANUM: CharList;\n    MARK: CharList;\n    USERINFO_CHARS: CharList;\n    URL_CHAR: CharList;\n    HEX: CharList;\n    TOKEN: CharList;\n    HEADER_CHARS: CharList;\n    CONNECTION_TOKEN_CHARS: CharList;\n    QUOTED_STRING: CharList;\n    HTAB_SP_VCHAR_OBS_TEXT: CharList;\n    MAJOR: {\n        0: number;\n        1: number;\n        2: number;\n        3: number;\n        4: number;\n        5: number;\n        6: number;\n        7: number;\n        8: number;\n        9: number;\n    };\n    MINOR: {\n        0: number;\n        1: number;\n        2: number;\n        3: number;\n        4: number;\n        5: number;\n        6: number;\n        7: number;\n        8: number;\n        9: number;\n    };\n    SPECIAL_HEADERS: {\n        connection: number;\n        'content-length': number;\n        'proxy-connection': number;\n        'transfer-encoding': number;\n        upgrade: number;\n    };\n    METHODS_HTTP: number[];\n    METHODS_ICE: number[];\n    METHODS_RTSP: number[];\n    METHOD_MAP: IntDict;\n    H_METHOD_MAP: {\n        [k: string]: number;\n    };\n    STATUSES_HTTP: number[];\n};\nexport default _default;\n"
  },
  {
    "path": "lib/llhttp/constants.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.SPECIAL_HEADERS = exports.MINOR = exports.MAJOR = exports.HTAB_SP_VCHAR_OBS_TEXT = exports.QUOTED_STRING = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.HEX = exports.URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.STATUSES_HTTP = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.HEADER_STATE = exports.FINISH = exports.STATUSES = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0;\nconst utils_1 = require(\"./utils\");\n// Emums\nexports.ERROR = {\n    OK: 0,\n    INTERNAL: 1,\n    STRICT: 2,\n    CR_EXPECTED: 25,\n    LF_EXPECTED: 3,\n    UNEXPECTED_CONTENT_LENGTH: 4,\n    UNEXPECTED_SPACE: 30,\n    CLOSED_CONNECTION: 5,\n    INVALID_METHOD: 6,\n    INVALID_URL: 7,\n    INVALID_CONSTANT: 8,\n    INVALID_VERSION: 9,\n    INVALID_HEADER_TOKEN: 10,\n    INVALID_CONTENT_LENGTH: 11,\n    INVALID_CHUNK_SIZE: 12,\n    INVALID_STATUS: 13,\n    INVALID_EOF_STATE: 14,\n    INVALID_TRANSFER_ENCODING: 15,\n    CB_MESSAGE_BEGIN: 16,\n    CB_HEADERS_COMPLETE: 17,\n    CB_MESSAGE_COMPLETE: 18,\n    CB_CHUNK_HEADER: 19,\n    CB_CHUNK_COMPLETE: 20,\n    PAUSED: 21,\n    PAUSED_UPGRADE: 22,\n    PAUSED_H2_UPGRADE: 23,\n    USER: 24,\n    CB_URL_COMPLETE: 26,\n    CB_STATUS_COMPLETE: 27,\n    CB_METHOD_COMPLETE: 32,\n    CB_VERSION_COMPLETE: 33,\n    CB_HEADER_FIELD_COMPLETE: 28,\n    CB_HEADER_VALUE_COMPLETE: 29,\n    CB_CHUNK_EXTENSION_NAME_COMPLETE: 34,\n    CB_CHUNK_EXTENSION_VALUE_COMPLETE: 35,\n    CB_RESET: 31,\n    CB_PROTOCOL_COMPLETE: 38,\n};\nexports.TYPE = {\n    BOTH: 0, // default\n    REQUEST: 1,\n    RESPONSE: 2,\n};\nexports.FLAGS = {\n    CONNECTION_KEEP_ALIVE: 1 << 0,\n    CONNECTION_CLOSE: 1 << 1,\n    CONNECTION_UPGRADE: 1 << 2,\n    CHUNKED: 1 << 3,\n    UPGRADE: 1 << 4,\n    CONTENT_LENGTH: 1 << 5,\n    SKIPBODY: 1 << 6,\n    TRAILING: 1 << 7,\n    // 1 << 8 is unused\n    TRANSFER_ENCODING: 1 << 9,\n};\nexports.LENIENT_FLAGS = {\n    HEADERS: 1 << 0,\n    CHUNKED_LENGTH: 1 << 1,\n    KEEP_ALIVE: 1 << 2,\n    TRANSFER_ENCODING: 1 << 3,\n    VERSION: 1 << 4,\n    DATA_AFTER_CLOSE: 1 << 5,\n    OPTIONAL_LF_AFTER_CR: 1 << 6,\n    OPTIONAL_CRLF_AFTER_CHUNK: 1 << 7,\n    OPTIONAL_CR_BEFORE_LF: 1 << 8,\n    SPACES_AFTER_CHUNK_SIZE: 1 << 9,\n};\nexports.METHODS = {\n    'DELETE': 0,\n    'GET': 1,\n    'HEAD': 2,\n    'POST': 3,\n    'PUT': 4,\n    /* pathological */\n    'CONNECT': 5,\n    'OPTIONS': 6,\n    'TRACE': 7,\n    /* WebDAV */\n    'COPY': 8,\n    'LOCK': 9,\n    'MKCOL': 10,\n    'MOVE': 11,\n    'PROPFIND': 12,\n    'PROPPATCH': 13,\n    'SEARCH': 14,\n    'UNLOCK': 15,\n    'BIND': 16,\n    'REBIND': 17,\n    'UNBIND': 18,\n    'ACL': 19,\n    /* subversion */\n    'REPORT': 20,\n    'MKACTIVITY': 21,\n    'CHECKOUT': 22,\n    'MERGE': 23,\n    /* upnp */\n    'M-SEARCH': 24,\n    'NOTIFY': 25,\n    'SUBSCRIBE': 26,\n    'UNSUBSCRIBE': 27,\n    /* RFC-5789 */\n    'PATCH': 28,\n    'PURGE': 29,\n    /* CalDAV */\n    'MKCALENDAR': 30,\n    /* RFC-2068, section 19.6.1.2 */\n    'LINK': 31,\n    'UNLINK': 32,\n    /* icecast */\n    'SOURCE': 33,\n    /* RFC-7540, section 11.6 */\n    'PRI': 34,\n    /* RFC-2326 RTSP */\n    'DESCRIBE': 35,\n    'ANNOUNCE': 36,\n    'SETUP': 37,\n    'PLAY': 38,\n    'PAUSE': 39,\n    'TEARDOWN': 40,\n    'GET_PARAMETER': 41,\n    'SET_PARAMETER': 42,\n    'REDIRECT': 43,\n    'RECORD': 44,\n    /* RAOP */\n    'FLUSH': 45,\n    /* DRAFT https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html */\n    'QUERY': 46,\n};\nexports.STATUSES = {\n    CONTINUE: 100,\n    SWITCHING_PROTOCOLS: 101,\n    PROCESSING: 102,\n    EARLY_HINTS: 103,\n    RESPONSE_IS_STALE: 110, // Unofficial\n    REVALIDATION_FAILED: 111, // Unofficial\n    DISCONNECTED_OPERATION: 112, // Unofficial\n    HEURISTIC_EXPIRATION: 113, // Unofficial\n    MISCELLANEOUS_WARNING: 199, // Unofficial\n    OK: 200,\n    CREATED: 201,\n    ACCEPTED: 202,\n    NON_AUTHORITATIVE_INFORMATION: 203,\n    NO_CONTENT: 204,\n    RESET_CONTENT: 205,\n    PARTIAL_CONTENT: 206,\n    MULTI_STATUS: 207,\n    ALREADY_REPORTED: 208,\n    TRANSFORMATION_APPLIED: 214, // Unofficial\n    IM_USED: 226,\n    MISCELLANEOUS_PERSISTENT_WARNING: 299, // Unofficial\n    MULTIPLE_CHOICES: 300,\n    MOVED_PERMANENTLY: 301,\n    FOUND: 302,\n    SEE_OTHER: 303,\n    NOT_MODIFIED: 304,\n    USE_PROXY: 305,\n    SWITCH_PROXY: 306, // No longer used\n    TEMPORARY_REDIRECT: 307,\n    PERMANENT_REDIRECT: 308,\n    BAD_REQUEST: 400,\n    UNAUTHORIZED: 401,\n    PAYMENT_REQUIRED: 402,\n    FORBIDDEN: 403,\n    NOT_FOUND: 404,\n    METHOD_NOT_ALLOWED: 405,\n    NOT_ACCEPTABLE: 406,\n    PROXY_AUTHENTICATION_REQUIRED: 407,\n    REQUEST_TIMEOUT: 408,\n    CONFLICT: 409,\n    GONE: 410,\n    LENGTH_REQUIRED: 411,\n    PRECONDITION_FAILED: 412,\n    PAYLOAD_TOO_LARGE: 413,\n    URI_TOO_LONG: 414,\n    UNSUPPORTED_MEDIA_TYPE: 415,\n    RANGE_NOT_SATISFIABLE: 416,\n    EXPECTATION_FAILED: 417,\n    IM_A_TEAPOT: 418,\n    PAGE_EXPIRED: 419, // Unofficial\n    ENHANCE_YOUR_CALM: 420, // Unofficial\n    MISDIRECTED_REQUEST: 421,\n    UNPROCESSABLE_ENTITY: 422,\n    LOCKED: 423,\n    FAILED_DEPENDENCY: 424,\n    TOO_EARLY: 425,\n    UPGRADE_REQUIRED: 426,\n    PRECONDITION_REQUIRED: 428,\n    TOO_MANY_REQUESTS: 429,\n    REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL: 430, // Unofficial\n    REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n    LOGIN_TIMEOUT: 440, // Unofficial\n    NO_RESPONSE: 444, // Unofficial\n    RETRY_WITH: 449, // Unofficial\n    BLOCKED_BY_PARENTAL_CONTROL: 450, // Unofficial\n    UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n    CLIENT_CLOSED_LOAD_BALANCED_REQUEST: 460, // Unofficial\n    INVALID_X_FORWARDED_FOR: 463, // Unofficial\n    REQUEST_HEADER_TOO_LARGE: 494, // Unofficial\n    SSL_CERTIFICATE_ERROR: 495, // Unofficial\n    SSL_CERTIFICATE_REQUIRED: 496, // Unofficial\n    HTTP_REQUEST_SENT_TO_HTTPS_PORT: 497, // Unofficial\n    INVALID_TOKEN: 498, // Unofficial\n    CLIENT_CLOSED_REQUEST: 499, // Unofficial\n    INTERNAL_SERVER_ERROR: 500,\n    NOT_IMPLEMENTED: 501,\n    BAD_GATEWAY: 502,\n    SERVICE_UNAVAILABLE: 503,\n    GATEWAY_TIMEOUT: 504,\n    HTTP_VERSION_NOT_SUPPORTED: 505,\n    VARIANT_ALSO_NEGOTIATES: 506,\n    INSUFFICIENT_STORAGE: 507,\n    LOOP_DETECTED: 508,\n    BANDWIDTH_LIMIT_EXCEEDED: 509,\n    NOT_EXTENDED: 510,\n    NETWORK_AUTHENTICATION_REQUIRED: 511,\n    WEB_SERVER_UNKNOWN_ERROR: 520, // Unofficial\n    WEB_SERVER_IS_DOWN: 521, // Unofficial\n    CONNECTION_TIMEOUT: 522, // Unofficial\n    ORIGIN_IS_UNREACHABLE: 523, // Unofficial\n    TIMEOUT_OCCURED: 524, // Unofficial\n    SSL_HANDSHAKE_FAILED: 525, // Unofficial\n    INVALID_SSL_CERTIFICATE: 526, // Unofficial\n    RAILGUN_ERROR: 527, // Unofficial\n    SITE_IS_OVERLOADED: 529, // Unofficial\n    SITE_IS_FROZEN: 530, // Unofficial\n    IDENTITY_PROVIDER_AUTHENTICATION_ERROR: 561, // Unofficial\n    NETWORK_READ_TIMEOUT: 598, // Unofficial\n    NETWORK_CONNECT_TIMEOUT: 599, // Unofficial\n};\nexports.FINISH = {\n    SAFE: 0,\n    SAFE_WITH_CB: 1,\n    UNSAFE: 2,\n};\nexports.HEADER_STATE = {\n    GENERAL: 0,\n    CONNECTION: 1,\n    CONTENT_LENGTH: 2,\n    TRANSFER_ENCODING: 3,\n    UPGRADE: 4,\n    CONNECTION_KEEP_ALIVE: 5,\n    CONNECTION_CLOSE: 6,\n    CONNECTION_UPGRADE: 7,\n    TRANSFER_ENCODING_CHUNKED: 8,\n};\n// C headers\nexports.METHODS_HTTP = [\n    exports.METHODS.DELETE,\n    exports.METHODS.GET,\n    exports.METHODS.HEAD,\n    exports.METHODS.POST,\n    exports.METHODS.PUT,\n    exports.METHODS.CONNECT,\n    exports.METHODS.OPTIONS,\n    exports.METHODS.TRACE,\n    exports.METHODS.COPY,\n    exports.METHODS.LOCK,\n    exports.METHODS.MKCOL,\n    exports.METHODS.MOVE,\n    exports.METHODS.PROPFIND,\n    exports.METHODS.PROPPATCH,\n    exports.METHODS.SEARCH,\n    exports.METHODS.UNLOCK,\n    exports.METHODS.BIND,\n    exports.METHODS.REBIND,\n    exports.METHODS.UNBIND,\n    exports.METHODS.ACL,\n    exports.METHODS.REPORT,\n    exports.METHODS.MKACTIVITY,\n    exports.METHODS.CHECKOUT,\n    exports.METHODS.MERGE,\n    exports.METHODS['M-SEARCH'],\n    exports.METHODS.NOTIFY,\n    exports.METHODS.SUBSCRIBE,\n    exports.METHODS.UNSUBSCRIBE,\n    exports.METHODS.PATCH,\n    exports.METHODS.PURGE,\n    exports.METHODS.MKCALENDAR,\n    exports.METHODS.LINK,\n    exports.METHODS.UNLINK,\n    exports.METHODS.PRI,\n    // TODO(indutny): should we allow it with HTTP?\n    exports.METHODS.SOURCE,\n    exports.METHODS.QUERY,\n];\nexports.METHODS_ICE = [\n    exports.METHODS.SOURCE,\n];\nexports.METHODS_RTSP = [\n    exports.METHODS.OPTIONS,\n    exports.METHODS.DESCRIBE,\n    exports.METHODS.ANNOUNCE,\n    exports.METHODS.SETUP,\n    exports.METHODS.PLAY,\n    exports.METHODS.PAUSE,\n    exports.METHODS.TEARDOWN,\n    exports.METHODS.GET_PARAMETER,\n    exports.METHODS.SET_PARAMETER,\n    exports.METHODS.REDIRECT,\n    exports.METHODS.RECORD,\n    exports.METHODS.FLUSH,\n    // For AirPlay\n    exports.METHODS.GET,\n    exports.METHODS.POST,\n];\nexports.METHOD_MAP = (0, utils_1.enumToMap)(exports.METHODS);\nexports.H_METHOD_MAP = Object.fromEntries(Object.entries(exports.METHODS).filter(([k]) => k.startsWith('H')));\nexports.STATUSES_HTTP = [\n    exports.STATUSES.CONTINUE,\n    exports.STATUSES.SWITCHING_PROTOCOLS,\n    exports.STATUSES.PROCESSING,\n    exports.STATUSES.EARLY_HINTS,\n    exports.STATUSES.RESPONSE_IS_STALE,\n    exports.STATUSES.REVALIDATION_FAILED,\n    exports.STATUSES.DISCONNECTED_OPERATION,\n    exports.STATUSES.HEURISTIC_EXPIRATION,\n    exports.STATUSES.MISCELLANEOUS_WARNING,\n    exports.STATUSES.OK,\n    exports.STATUSES.CREATED,\n    exports.STATUSES.ACCEPTED,\n    exports.STATUSES.NON_AUTHORITATIVE_INFORMATION,\n    exports.STATUSES.NO_CONTENT,\n    exports.STATUSES.RESET_CONTENT,\n    exports.STATUSES.PARTIAL_CONTENT,\n    exports.STATUSES.MULTI_STATUS,\n    exports.STATUSES.ALREADY_REPORTED,\n    exports.STATUSES.TRANSFORMATION_APPLIED,\n    exports.STATUSES.IM_USED,\n    exports.STATUSES.MISCELLANEOUS_PERSISTENT_WARNING,\n    exports.STATUSES.MULTIPLE_CHOICES,\n    exports.STATUSES.MOVED_PERMANENTLY,\n    exports.STATUSES.FOUND,\n    exports.STATUSES.SEE_OTHER,\n    exports.STATUSES.NOT_MODIFIED,\n    exports.STATUSES.USE_PROXY,\n    exports.STATUSES.SWITCH_PROXY,\n    exports.STATUSES.TEMPORARY_REDIRECT,\n    exports.STATUSES.PERMANENT_REDIRECT,\n    exports.STATUSES.BAD_REQUEST,\n    exports.STATUSES.UNAUTHORIZED,\n    exports.STATUSES.PAYMENT_REQUIRED,\n    exports.STATUSES.FORBIDDEN,\n    exports.STATUSES.NOT_FOUND,\n    exports.STATUSES.METHOD_NOT_ALLOWED,\n    exports.STATUSES.NOT_ACCEPTABLE,\n    exports.STATUSES.PROXY_AUTHENTICATION_REQUIRED,\n    exports.STATUSES.REQUEST_TIMEOUT,\n    exports.STATUSES.CONFLICT,\n    exports.STATUSES.GONE,\n    exports.STATUSES.LENGTH_REQUIRED,\n    exports.STATUSES.PRECONDITION_FAILED,\n    exports.STATUSES.PAYLOAD_TOO_LARGE,\n    exports.STATUSES.URI_TOO_LONG,\n    exports.STATUSES.UNSUPPORTED_MEDIA_TYPE,\n    exports.STATUSES.RANGE_NOT_SATISFIABLE,\n    exports.STATUSES.EXPECTATION_FAILED,\n    exports.STATUSES.IM_A_TEAPOT,\n    exports.STATUSES.PAGE_EXPIRED,\n    exports.STATUSES.ENHANCE_YOUR_CALM,\n    exports.STATUSES.MISDIRECTED_REQUEST,\n    exports.STATUSES.UNPROCESSABLE_ENTITY,\n    exports.STATUSES.LOCKED,\n    exports.STATUSES.FAILED_DEPENDENCY,\n    exports.STATUSES.TOO_EARLY,\n    exports.STATUSES.UPGRADE_REQUIRED,\n    exports.STATUSES.PRECONDITION_REQUIRED,\n    exports.STATUSES.TOO_MANY_REQUESTS,\n    exports.STATUSES.REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL,\n    exports.STATUSES.REQUEST_HEADER_FIELDS_TOO_LARGE,\n    exports.STATUSES.LOGIN_TIMEOUT,\n    exports.STATUSES.NO_RESPONSE,\n    exports.STATUSES.RETRY_WITH,\n    exports.STATUSES.BLOCKED_BY_PARENTAL_CONTROL,\n    exports.STATUSES.UNAVAILABLE_FOR_LEGAL_REASONS,\n    exports.STATUSES.CLIENT_CLOSED_LOAD_BALANCED_REQUEST,\n    exports.STATUSES.INVALID_X_FORWARDED_FOR,\n    exports.STATUSES.REQUEST_HEADER_TOO_LARGE,\n    exports.STATUSES.SSL_CERTIFICATE_ERROR,\n    exports.STATUSES.SSL_CERTIFICATE_REQUIRED,\n    exports.STATUSES.HTTP_REQUEST_SENT_TO_HTTPS_PORT,\n    exports.STATUSES.INVALID_TOKEN,\n    exports.STATUSES.CLIENT_CLOSED_REQUEST,\n    exports.STATUSES.INTERNAL_SERVER_ERROR,\n    exports.STATUSES.NOT_IMPLEMENTED,\n    exports.STATUSES.BAD_GATEWAY,\n    exports.STATUSES.SERVICE_UNAVAILABLE,\n    exports.STATUSES.GATEWAY_TIMEOUT,\n    exports.STATUSES.HTTP_VERSION_NOT_SUPPORTED,\n    exports.STATUSES.VARIANT_ALSO_NEGOTIATES,\n    exports.STATUSES.INSUFFICIENT_STORAGE,\n    exports.STATUSES.LOOP_DETECTED,\n    exports.STATUSES.BANDWIDTH_LIMIT_EXCEEDED,\n    exports.STATUSES.NOT_EXTENDED,\n    exports.STATUSES.NETWORK_AUTHENTICATION_REQUIRED,\n    exports.STATUSES.WEB_SERVER_UNKNOWN_ERROR,\n    exports.STATUSES.WEB_SERVER_IS_DOWN,\n    exports.STATUSES.CONNECTION_TIMEOUT,\n    exports.STATUSES.ORIGIN_IS_UNREACHABLE,\n    exports.STATUSES.TIMEOUT_OCCURED,\n    exports.STATUSES.SSL_HANDSHAKE_FAILED,\n    exports.STATUSES.INVALID_SSL_CERTIFICATE,\n    exports.STATUSES.RAILGUN_ERROR,\n    exports.STATUSES.SITE_IS_OVERLOADED,\n    exports.STATUSES.SITE_IS_FROZEN,\n    exports.STATUSES.IDENTITY_PROVIDER_AUTHENTICATION_ERROR,\n    exports.STATUSES.NETWORK_READ_TIMEOUT,\n    exports.STATUSES.NETWORK_CONNECT_TIMEOUT,\n];\nexports.ALPHA = [];\nfor (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {\n    // Upper case\n    exports.ALPHA.push(String.fromCharCode(i));\n    // Lower case\n    exports.ALPHA.push(String.fromCharCode(i + 0x20));\n}\nexports.NUM_MAP = {\n    0: 0, 1: 1, 2: 2, 3: 3, 4: 4,\n    5: 5, 6: 6, 7: 7, 8: 8, 9: 9,\n};\nexports.HEX_MAP = {\n    0: 0, 1: 1, 2: 2, 3: 3, 4: 4,\n    5: 5, 6: 6, 7: 7, 8: 8, 9: 9,\n    A: 0XA, B: 0XB, C: 0XC, D: 0XD, E: 0XE, F: 0XF,\n    a: 0xa, b: 0xb, c: 0xc, d: 0xd, e: 0xe, f: 0xf,\n};\nexports.NUM = [\n    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n];\nexports.ALPHANUM = exports.ALPHA.concat(exports.NUM);\nexports.MARK = ['-', '_', '.', '!', '~', '*', '\\'', '(', ')'];\nexports.USERINFO_CHARS = exports.ALPHANUM\n    .concat(exports.MARK)\n    .concat(['%', ';', ':', '&', '=', '+', '$', ',']);\n// TODO(indutny): use RFC\nexports.URL_CHAR = [\n    '!', '\"', '$', '%', '&', '\\'',\n    '(', ')', '*', '+', ',', '-', '.', '/',\n    ':', ';', '<', '=', '>',\n    '@', '[', '\\\\', ']', '^', '_',\n    '`',\n    '{', '|', '}', '~',\n].concat(exports.ALPHANUM);\nexports.HEX = exports.NUM.concat(['a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F']);\n/* Tokens as defined by rfc 2616. Also lowercases them.\n *        token       = 1*<any CHAR except CTLs or separators>\n *     separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                    | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                    | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                    | \"{\" | \"}\" | SP | HT\n */\nexports.TOKEN = [\n    '!', '#', '$', '%', '&', '\\'',\n    '*', '+', '-', '.',\n    '^', '_', '`',\n    '|', '~',\n].concat(exports.ALPHANUM);\n/*\n * Verify that a char is a valid visible (printable) US-ASCII\n * character or %x80-FF\n */\nexports.HEADER_CHARS = ['\\t'];\nfor (let i = 32; i <= 255; i++) {\n    if (i !== 127) {\n        exports.HEADER_CHARS.push(i);\n    }\n}\n// ',' = \\x44\nexports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS.filter((c) => c !== 44);\nexports.QUOTED_STRING = ['\\t', ' '];\nfor (let i = 0x21; i <= 0xff; i++) {\n    if (i !== 0x22 && i !== 0x5c) { // All characters in ASCII except \\ and \"\n        exports.QUOTED_STRING.push(i);\n    }\n}\nexports.HTAB_SP_VCHAR_OBS_TEXT = ['\\t', ' '];\n// VCHAR: https://tools.ietf.org/html/rfc5234#appendix-B.1\nfor (let i = 0x21; i <= 0x7E; i++) {\n    exports.HTAB_SP_VCHAR_OBS_TEXT.push(i);\n}\n// OBS_TEXT: https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf\nfor (let i = 0x80; i <= 0xff; i++) {\n    exports.HTAB_SP_VCHAR_OBS_TEXT.push(i);\n}\nexports.MAJOR = exports.NUM_MAP;\nexports.MINOR = exports.MAJOR;\nexports.SPECIAL_HEADERS = {\n    'connection': exports.HEADER_STATE.CONNECTION,\n    'content-length': exports.HEADER_STATE.CONTENT_LENGTH,\n    'proxy-connection': exports.HEADER_STATE.CONNECTION,\n    'transfer-encoding': exports.HEADER_STATE.TRANSFER_ENCODING,\n    'upgrade': exports.HEADER_STATE.UPGRADE,\n};\nexports.default = {\n    ERROR: exports.ERROR,\n    TYPE: exports.TYPE,\n    FLAGS: exports.FLAGS,\n    LENIENT_FLAGS: exports.LENIENT_FLAGS,\n    METHODS: exports.METHODS,\n    STATUSES: exports.STATUSES,\n    FINISH: exports.FINISH,\n    HEADER_STATE: exports.HEADER_STATE,\n    ALPHA: exports.ALPHA,\n    NUM_MAP: exports.NUM_MAP,\n    HEX_MAP: exports.HEX_MAP,\n    NUM: exports.NUM,\n    ALPHANUM: exports.ALPHANUM,\n    MARK: exports.MARK,\n    USERINFO_CHARS: exports.USERINFO_CHARS,\n    URL_CHAR: exports.URL_CHAR,\n    HEX: exports.HEX,\n    TOKEN: exports.TOKEN,\n    HEADER_CHARS: exports.HEADER_CHARS,\n    CONNECTION_TOKEN_CHARS: exports.CONNECTION_TOKEN_CHARS,\n    QUOTED_STRING: exports.QUOTED_STRING,\n    HTAB_SP_VCHAR_OBS_TEXT: exports.HTAB_SP_VCHAR_OBS_TEXT,\n    MAJOR: exports.MAJOR,\n    MINOR: exports.MINOR,\n    SPECIAL_HEADERS: exports.SPECIAL_HEADERS,\n    METHODS_HTTP: exports.METHODS_HTTP,\n    METHODS_ICE: exports.METHODS_ICE,\n    METHODS_RTSP: exports.METHODS_RTSP,\n    METHOD_MAP: exports.METHOD_MAP,\n    H_METHOD_MAP: exports.H_METHOD_MAP,\n    STATUSES_HTTP: exports.STATUSES_HTTP,\n};\n"
  },
  {
    "path": "lib/llhttp/llhttp-wasm.js",
    "content": "'use strict'\n\nconst { Buffer } = require('node:buffer')\n\nconst wasmBase64 = 'AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAn9/AGABfwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAzU0BQYAAAMAAAAAAAADAQMAAwMDAAACAAAAAAICAgICAgICAgIBAQEBAQEBAQEBAwAAAwAAAAQFAXABExMFAwEAAgYIAX8BQcDZBAsHxQcoBm1lbW9yeQIAC19pbml0aWFsaXplAAgZX19pbmRpcmVjdF9mdW5jdGlvbl90YWJsZQEAC2xsaHR0cF9pbml0AAkYbGxodHRwX3Nob3VsZF9rZWVwX2FsaXZlADcMbGxodHRwX2FsbG9jAAsGbWFsbG9jADkLbGxodHRwX2ZyZWUADARmcmVlAAwPbGxodHRwX2dldF90eXBlAA0VbGxodHRwX2dldF9odHRwX21ham9yAA4VbGxodHRwX2dldF9odHRwX21pbm9yAA8RbGxodHRwX2dldF9tZXRob2QAEBZsbGh0dHBfZ2V0X3N0YXR1c19jb2RlABESbGxodHRwX2dldF91cGdyYWRlABIMbGxodHRwX3Jlc2V0ABMObGxodHRwX2V4ZWN1dGUAFBRsbGh0dHBfc2V0dGluZ3NfaW5pdAAVDWxsaHR0cF9maW5pc2gAFgxsbGh0dHBfcGF1c2UAFw1sbGh0dHBfcmVzdW1lABgbbGxodHRwX3Jlc3VtZV9hZnRlcl91cGdyYWRlABkQbGxodHRwX2dldF9lcnJubwAaF2xsaHR0cF9nZXRfZXJyb3JfcmVhc29uABsXbGxodHRwX3NldF9lcnJvcl9yZWFzb24AHBRsbGh0dHBfZ2V0X2Vycm9yX3BvcwAdEWxsaHR0cF9lcnJub19uYW1lAB4SbGxodHRwX21ldGhvZF9uYW1lAB8SbGxodHRwX3N0YXR1c19uYW1lACAabGxodHRwX3NldF9sZW5pZW50X2hlYWRlcnMAISFsbGh0dHBfc2V0X2xlbmllbnRfY2h1bmtlZF9sZW5ndGgAIh1sbGh0dHBfc2V0X2xlbmllbnRfa2VlcF9hbGl2ZQAjJGxsaHR0cF9zZXRfbGVuaWVudF90cmFuc2Zlcl9lbmNvZGluZwAkGmxsaHR0cF9zZXRfbGVuaWVudF92ZXJzaW9uACUjbGxodHRwX3NldF9sZW5pZW50X2RhdGFfYWZ0ZXJfY2xvc2UAJidsbGh0dHBfc2V0X2xlbmllbnRfb3B0aW9uYWxfbGZfYWZ0ZXJfY3IAJyxsbGh0dHBfc2V0X2xlbmllbnRfb3B0aW9uYWxfY3JsZl9hZnRlcl9jaHVuawAoKGxsaHR0cF9zZXRfbGVuaWVudF9vcHRpb25hbF9jcl9iZWZvcmVfbGYAKSpsbGh0dHBfc2V0X2xlbmllbnRfc3BhY2VzX2FmdGVyX2NodW5rX3NpemUAKhhsbGh0dHBfbWVzc2FnZV9uZWVkc19lb2YANgkYAQBBAQsSAQIDBAUKBgcyNDMuKy8tLDAxCq/ZAjQWAEHA1QAoAgAEQAALQcDVAEEBNgIACxQAIAAQOCAAIAI2AjggACABOgAoCxQAIAAgAC8BNCAALQAwIAAQNxAACx4BAX9BwAAQOiIBEDggAUGACDYCOCABIAA6ACggAQuPDAEHfwJAIABFDQAgAEEIayIBIABBBGsoAgAiAEF4cSIEaiEFAkAgAEEBcQ0AIABBA3FFDQEgASABKAIAIgBrIgFB1NUAKAIASQ0BIAAgBGohBAJAAkBB2NUAKAIAIAFHBEAgAEH/AU0EQCAAQQN2IQMgASgCCCIAIAEoAgwiAkYEQEHE1QBBxNUAKAIAQX4gA3dxNgIADAULIAIgADYCCCAAIAI2AgwMBAsgASgCGCEGIAEgASgCDCIARwRAIAAgASgCCCICNgIIIAIgADYCDAwDCyABQRRqIgMoAgAiAkUEQCABKAIQIgJFDQIgAUEQaiEDCwNAIAMhByACIgBBFGoiAygCACICDQAgAEEQaiEDIAAoAhAiAg0ACyAHQQA2AgAMAgsgBSgCBCIAQQNxQQNHDQIgBSAAQX5xNgIEQczVACAENgIAIAUgBDYCACABIARBAXI2AgQMAwtBACEACyAGRQ0AAkAgASgCHCICQQJ0QfTXAGoiAygCACABRgRAIAMgADYCACAADQFByNUAQcjVACgCAEF+IAJ3cTYCAAwCCyAGQRBBFCAGKAIQIAFGG2ogADYCACAARQ0BCyAAIAY2AhggASgCECICBEAgACACNgIQIAIgADYCGAsgAUEUaigCACICRQ0AIABBFGogAjYCACACIAA2AhgLIAEgBU8NACAFKAIEIgBBAXFFDQACQAJAAkACQCAAQQJxRQRAQdzVACgCACAFRgRAQdzVACABNgIAQdDVAEHQ1QAoAgAgBGoiADYCACABIABBAXI2AgQgAUHY1QAoAgBHDQZBzNUAQQA2AgBB2NUAQQA2AgAMBgtB2NUAKAIAIAVGBEBB2NUAIAE2AgBBzNUAQczVACgCACAEaiIANgIAIAEgAEEBcjYCBCAAIAFqIAA2AgAMBgsgAEF4cSAEaiEEIABB/wFNBEAgAEEDdiEDIAUoAggiACAFKAIMIgJGBEBBxNUAQcTVACgCAEF+IAN3cTYCAAwFCyACIAA2AgggACACNgIMDAQLIAUoAhghBiAFIAUoAgwiAEcEQEHU1QAoAgAaIAAgBSgCCCICNgIIIAIgADYCDAwDCyAFQRRqIgMoAgAiAkUEQCAFKAIQIgJFDQIgBUEQaiEDCwNAIAMhByACIgBBFGoiAygCACICDQAgAEEQaiEDIAAoAhAiAg0ACyAHQQA2AgAMAgsgBSAAQX5xNgIEIAEgBGogBDYCACABIARBAXI2AgQMAwtBACEACyAGRQ0AAkAgBSgCHCICQQJ0QfTXAGoiAygCACAFRgRAIAMgADYCACAADQFByNUAQcjVACgCAEF+IAJ3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogADYCACAARQ0BCyAAIAY2AhggBSgCECICBEAgACACNgIQIAIgADYCGAsgBUEUaigCACICRQ0AIABBFGogAjYCACACIAA2AhgLIAEgBGogBDYCACABIARBAXI2AgQgAUHY1QAoAgBHDQBBzNUAIAQ2AgAMAQsgBEH/AU0EQCAEQXhxQezVAGohAAJ/QcTVACgCACICQQEgBEEDdnQiA3FFBEBBxNUAIAIgA3I2AgAgAAwBCyAAKAIICyICIAE2AgwgACABNgIIIAEgADYCDCABIAI2AggMAQtBHyECIARB////B00EQCAEQSYgBEEIdmciAGt2QQFxIABBAXRrQT5qIQILIAEgAjYCHCABQgA3AhAgAkECdEH01wBqIQACQEHI1QAoAgAiA0EBIAJ0IgdxRQRAIAAgATYCAEHI1QAgAyAHcjYCACABIAA2AhggASABNgIIIAEgATYCDAwBCyAEQRkgAkEBdmtBACACQR9HG3QhAiAAKAIAIQACQANAIAAiAygCBEF4cSAERg0BIAJBHXYhACACQQF0IQIgAyAAQQRxakEQaiIHKAIAIgANAAsgByABNgIAIAEgAzYCGCABIAE2AgwgASABNgIIDAELIAMoAggiACABNgIMIAMgATYCCCABQQA2AhggASADNgIMIAEgADYCCAtB5NUAQeTVACgCAEEBayIAQX8gABs2AgALCwcAIAAtACgLBwAgAC0AKgsHACAALQArCwcAIAAtACkLBwAgAC8BNAsHACAALQAwC0ABBH8gACgCGCEBIAAvAS4hAiAALQAoIQMgACgCOCEEIAAQOCAAIAQ2AjggACADOgAoIAAgAjsBLiAAIAE2AhgL5YUCAgd/A34gASACaiEEAkAgACIDKAIMIgANACADKAIEBEAgAyABNgIECyMAQRBrIgkkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAygCHCICQQJrDvwBAfkBAgMEBQYHCAkKCwwNDg8QERL4ARP3ARQV9gEWF/UBGBkaGxwdHh8g/QH7ASH0ASIjJCUmJygpKivzASwtLi8wMTLyAfEBMzTwAe8BNTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5P+gFQUVJT7gHtAVTsAVXrAVZXWFla6gFbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUBtgG3AbgBuQG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAccByAHJAcoBywHMAc0BzgHpAegBzwHnAdAB5gHRAdIB0wHUAeUB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMBAPwBC0EADOMBC0EODOIBC0ENDOEBC0EPDOABC0EQDN8BC0ETDN4BC0EUDN0BC0EVDNwBC0EWDNsBC0EXDNoBC0EYDNkBC0EZDNgBC0EaDNcBC0EbDNYBC0EcDNUBC0EdDNQBC0EeDNMBC0EfDNIBC0EgDNEBC0EhDNABC0EIDM8BC0EiDM4BC0EkDM0BC0EjDMwBC0EHDMsBC0ElDMoBC0EmDMkBC0EnDMgBC0EoDMcBC0ESDMYBC0ERDMUBC0EpDMQBC0EqDMMBC0ErDMIBC0EsDMEBC0HeAQzAAQtBLgy/AQtBLwy+AQtBMAy9AQtBMQy8AQtBMgy7AQtBMwy6AQtBNAy5AQtB3wEMuAELQTUMtwELQTkMtgELQQwMtQELQTYMtAELQTcMswELQTgMsgELQT4MsQELQToMsAELQeABDK8BC0ELDK4BC0E/DK0BC0E7DKwBC0EKDKsBC0E8DKoBC0E9DKkBC0HhAQyoAQtBwQAMpwELQcAADKYBC0HCAAylAQtBCQykAQtBLQyjAQtBwwAMogELQcQADKEBC0HFAAygAQtBxgAMnwELQccADJ4BC0HIAAydAQtByQAMnAELQcoADJsBC0HLAAyaAQtBzAAMmQELQc0ADJgBC0HOAAyXAQtBzwAMlgELQdAADJUBC0HRAAyUAQtB0gAMkwELQdMADJIBC0HVAAyRAQtB1AAMkAELQdYADI8BC0HXAAyOAQtB2AAMjQELQdkADIwBC0HaAAyLAQtB2wAMigELQdwADIkBC0HdAAyIAQtB3gAMhwELQd8ADIYBC0HgAAyFAQtB4QAMhAELQeIADIMBC0HjAAyCAQtB5AAMgQELQeUADIABC0HiAQx/C0HmAAx+C0HnAAx9C0EGDHwLQegADHsLQQUMegtB6QAMeQtBBAx4C0HqAAx3C0HrAAx2C0HsAAx1C0HtAAx0C0EDDHMLQe4ADHILQe8ADHELQfAADHALQfIADG8LQfEADG4LQfMADG0LQfQADGwLQfUADGsLQfYADGoLQQIMaQtB9wAMaAtB+AAMZwtB+QAMZgtB+gAMZQtB+wAMZAtB/AAMYwtB/QAMYgtB/gAMYQtB/wAMYAtBgAEMXwtBgQEMXgtBggEMXQtBgwEMXAtBhAEMWwtBhQEMWgtBhgEMWQtBhwEMWAtBiAEMVwtBiQEMVgtBigEMVQtBiwEMVAtBjAEMUwtBjQEMUgtBjgEMUQtBjwEMUAtBkAEMTwtBkQEMTgtBkgEMTQtBkwEMTAtBlAEMSwtBlQEMSgtBlgEMSQtBlwEMSAtBmAEMRwtBmQEMRgtBmgEMRQtBmwEMRAtBnAEMQwtBnQEMQgtBngEMQQtBnwEMQAtBoAEMPwtBoQEMPgtBogEMPQtBowEMPAtBpAEMOwtBpQEMOgtBpgEMOQtBpwEMOAtBqAEMNwtBqQEMNgtBqgEMNQtBqwEMNAtBrAEMMwtBrQEMMgtBrgEMMQtBrwEMMAtBsAEMLwtBsQEMLgtBsgEMLQtBswEMLAtBtAEMKwtBtQEMKgtBtgEMKQtBtwEMKAtBuAEMJwtBuQEMJgtBugEMJQtBuwEMJAtBvAEMIwtBvQEMIgtBvgEMIQtBvwEMIAtBwAEMHwtBwQEMHgtBwgEMHQtBAQwcC0HDAQwbC0HEAQwaC0HFAQwZC0HGAQwYC0HHAQwXC0HIAQwWC0HJAQwVC0HKAQwUC0HLAQwTC0HMAQwSC0HNAQwRC0HOAQwQC0HPAQwPC0HQAQwOC0HRAQwNC0HSAQwMC0HTAQwLC0HUAQwKC0HVAQwJC0HWAQwIC0HjAQwHC0HXAQwGC0HYAQwFC0HZAQwEC0HaAQwDC0HbAQwCC0HdAQwBC0HcAQshAgNAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJ/AkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAMCfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAg7jAQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEjJCUnKCmeA5sDmgORA4oDgwOAA/0C+wL4AvIC8QLvAu0C6ALnAuYC5QLkAtwC2wLaAtkC2ALXAtYC1QLPAs4CzALLAsoCyQLIAscCxgLEAsMCvgK8AroCuQK4ArcCtgK1ArQCswKyArECsAKuAq0CqQKoAqcCpgKlAqQCowKiAqECoAKfApgCkAKMAosCigKBAv4B/QH8AfsB+gH5AfgB9wH1AfMB8AHrAekB6AHnAeYB5QHkAeMB4gHhAeAB3wHeAd0B3AHaAdkB2AHXAdYB1QHUAdMB0gHRAdABzwHOAc0BzAHLAcoByQHIAccBxgHFAcQBwwHCAcEBwAG/Ab4BvQG8AbsBugG5AbgBtwG2AbUBtAGzAbIBsQGwAa8BrgGtAawBqwGqAakBqAGnAaYBpQGkAaMBogGfAZ4BmQGYAZcBlgGVAZQBkwGSAZEBkAGPAY0BjAGHAYYBhQGEAYMBggF9fHt6eXZ1dFBRUlNUVQsgASAERw1yQf0BIQIMvgMLIAEgBEcNmAFB2wEhAgy9AwsgASAERw3xAUGOASECDLwDCyABIARHDfwBQYQBIQIMuwMLIAEgBEcNigJB/wAhAgy6AwsgASAERw2RAkH9ACECDLkDCyABIARHDZQCQfsAIQIMuAMLIAEgBEcNHkEeIQIMtwMLIAEgBEcNGUEYIQIMtgMLIAEgBEcNygJBzQAhAgy1AwsgASAERw3VAkHGACECDLQDCyABIARHDdYCQcMAIQIMswMLIAEgBEcN3AJBOCECDLIDCyADLQAwQQFGDa0DDIkDC0EAIQACQAJAAkAgAy0AKkUNACADLQArRQ0AIAMvATIiAkECcUUNAQwCCyADLwEyIgJBAXFFDQELQQEhACADLQAoQQFGDQAgAy8BNCIGQeQAa0HkAEkNACAGQcwBRg0AIAZBsAJGDQAgAkHAAHENAEEAIQAgAkGIBHFBgARGDQAgAkEocUEARyEACyADQQA7ATIgA0EAOgAxAkAgAEUEQCADQQA6ADEgAy0ALkEEcQ0BDLEDCyADQgA3AyALIANBADoAMSADQQE6ADYMSAtBACEAAkAgAygCOCICRQ0AIAIoAjAiAkUNACADIAIRAAAhAAsgAEUNSCAAQRVHDWIgA0EENgIcIAMgATYCFCADQdIbNgIQIANBFTYCDEEAIQIMrwMLIAEgBEYEQEEGIQIMrwMLIAEtAABBCkcNGSABQQFqIQEMGgsgA0IANwMgQRIhAgyUAwsgASAERw2KA0EjIQIMrAMLIAEgBEYEQEEHIQIMrAMLAkACQCABLQAAQQprDgQBGBgAGAsgAUEBaiEBQRAhAgyTAwsgAUEBaiEBIANBL2otAABBAXENF0EAIQIgA0EANgIcIAMgATYCFCADQZkgNgIQIANBGTYCDAyrAwsgAyADKQMgIgwgBCABa60iCn0iC0IAIAsgDFgbNwMgIAogDFoNGEEIIQIMqgMLIAEgBEcEQCADQQk2AgggAyABNgIEQRQhAgyRAwtBCSECDKkDCyADKQMgUA2uAgxDCyABIARGBEBBCyECDKgDCyABLQAAQQpHDRYgAUEBaiEBDBcLIANBL2otAABBAXFFDRkMJgtBACEAAkAgAygCOCICRQ0AIAIoAlAiAkUNACADIAIRAAAhAAsgAA0ZDEILQQAhAAJAIAMoAjgiAkUNACACKAJQIgJFDQAgAyACEQAAIQALIAANGgwkC0EAIQACQCADKAI4IgJFDQAgAigCUCICRQ0AIAMgAhEAACEACyAADRsMMgsgA0Evai0AAEEBcUUNHAwiC0EAIQACQCADKAI4IgJFDQAgAigCVCICRQ0AIAMgAhEAACEACyAADRwMQgtBACEAAkAgAygCOCICRQ0AIAIoAlQiAkUNACADIAIRAAAhAAsgAA0dDCALIAEgBEYEQEETIQIMoAMLAkAgAS0AACIAQQprDgQfIyMAIgsgAUEBaiEBDB8LQQAhAAJAIAMoAjgiAkUNACACKAJUIgJFDQAgAyACEQAAIQALIAANIgxCCyABIARGBEBBFiECDJ4DCyABLQAAQcDBAGotAABBAUcNIwyDAwsCQANAIAEtAABBsDtqLQAAIgBBAUcEQAJAIABBAmsOAgMAJwsgAUEBaiEBQSEhAgyGAwsgBCABQQFqIgFHDQALQRghAgydAwsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAFBAWoiARA0IgANIQxBC0EAIQACQCADKAI4IgJFDQAgAigCVCICRQ0AIAMgAhEAACEACyAADSMMKgsgASAERgRAQRwhAgybAwsgA0EKNgIIIAMgATYCBEEAIQACQCADKAI4IgJFDQAgAigCUCICRQ0AIAMgAhEAACEACyAADSVBJCECDIEDCyABIARHBEADQCABLQAAQbA9ai0AACIAQQNHBEAgAEEBaw4FGBomggMlJgsgBCABQQFqIgFHDQALQRshAgyaAwtBGyECDJkDCwNAIAEtAABBsD9qLQAAIgBBA0cEQCAAQQFrDgUPEScTJicLIAQgAUEBaiIBRw0AC0EeIQIMmAMLIAEgBEcEQCADQQs2AgggAyABNgIEQQchAgz/AgtBHyECDJcDCyABIARGBEBBICECDJcDCwJAIAEtAABBDWsOFC4/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8APwtBACECIANBADYCHCADQb8LNgIQIANBAjYCDCADIAFBAWo2AhQMlgMLIANBL2ohAgNAIAEgBEYEQEEhIQIMlwMLAkACQAJAIAEtAAAiAEEJaw4YAgApKQEpKSkpKSkpKSkpKSkpKSkpKSkCJwsgAUEBaiEBIANBL2otAABBAXFFDQoMGAsgAUEBaiEBDBcLIAFBAWohASACLQAAQQJxDQALQQAhAiADQQA2AhwgAyABNgIUIANBnxU2AhAgA0EMNgIMDJUDCyADLQAuQYABcUUNAQtBACEAAkAgAygCOCICRQ0AIAIoAlwiAkUNACADIAIRAAAhAAsgAEUN5gIgAEEVRgRAIANBJDYCHCADIAE2AhQgA0GbGzYCECADQRU2AgxBACECDJQDC0EAIQIgA0EANgIcIAMgATYCFCADQZAONgIQIANBFDYCDAyTAwtBACECIANBADYCHCADIAE2AhQgA0G+IDYCECADQQI2AgwMkgMLIAMoAgQhAEEAIQIgA0EANgIEIAMgACABIAynaiIBEDIiAEUNKyADQQc2AhwgAyABNgIUIAMgADYCDAyRAwsgAy0ALkHAAHFFDQELQQAhAAJAIAMoAjgiAkUNACACKAJYIgJFDQAgAyACEQAAIQALIABFDSsgAEEVRgRAIANBCjYCHCADIAE2AhQgA0HrGTYCECADQRU2AgxBACECDJADC0EAIQIgA0EANgIcIAMgATYCFCADQZMMNgIQIANBEzYCDAyPAwtBACECIANBADYCHCADIAE2AhQgA0GCFTYCECADQQI2AgwMjgMLQQAhAiADQQA2AhwgAyABNgIUIANB3RQ2AhAgA0EZNgIMDI0DC0EAIQIgA0EANgIcIAMgATYCFCADQeYdNgIQIANBGTYCDAyMAwsgAEEVRg09QQAhAiADQQA2AhwgAyABNgIUIANB0A82AhAgA0EiNgIMDIsDCyADKAIEIQBBACECIANBADYCBCADIAAgARAzIgBFDSggA0ENNgIcIAMgATYCFCADIAA2AgwMigMLIABBFUYNOkEAIQIgA0EANgIcIAMgATYCFCADQdAPNgIQIANBIjYCDAyJAwsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQMyIARQRAIAFBAWohAQwoCyADQQ42AhwgAyAANgIMIAMgAUEBajYCFAyIAwsgAEEVRg03QQAhAiADQQA2AhwgAyABNgIUIANB0A82AhAgA0EiNgIMDIcDCyADKAIEIQBBACECIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDCcLIANBDzYCHCADIAA2AgwgAyABQQFqNgIUDIYDC0EAIQIgA0EANgIcIAMgATYCFCADQeIXNgIQIANBGTYCDAyFAwsgAEEVRg0zQQAhAiADQQA2AhwgAyABNgIUIANB1gw2AhAgA0EjNgIMDIQDCyADKAIEIQBBACECIANBADYCBCADIAAgARA0IgBFDSUgA0ERNgIcIAMgATYCFCADIAA2AgwMgwMLIABBFUYNMEEAIQIgA0EANgIcIAMgATYCFCADQdYMNgIQIANBIzYCDAyCAwsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQwlCyADQRI2AhwgAyAANgIMIAMgAUEBajYCFAyBAwsgA0Evai0AAEEBcUUNAQtBFyECDOYCC0EAIQIgA0EANgIcIAMgATYCFCADQeIXNgIQIANBGTYCDAz+AgsgAEE7Rw0AIAFBAWohAQwMC0EAIQIgA0EANgIcIAMgATYCFCADQZIYNgIQIANBAjYCDAz8AgsgAEEVRg0oQQAhAiADQQA2AhwgAyABNgIUIANB1gw2AhAgA0EjNgIMDPsCCyADQRQ2AhwgAyABNgIUIAMgADYCDAz6AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQz1AgsgA0EVNgIcIAMgADYCDCADIAFBAWo2AhQM+QILIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDQiAEUEQCABQQFqIQEM8wILIANBFzYCHCADIAA2AgwgAyABQQFqNgIUDPgCCyAAQRVGDSNBACECIANBADYCHCADIAE2AhQgA0HWDDYCECADQSM2AgwM9wILIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDQiAEUEQCABQQFqIQEMHQsgA0EZNgIcIAMgADYCDCADIAFBAWo2AhQM9gILIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDQiAEUEQCABQQFqIQEM7wILIANBGjYCHCADIAA2AgwgAyABQQFqNgIUDPUCCyAAQRVGDR9BACECIANBADYCHCADIAE2AhQgA0HQDzYCECADQSI2AgwM9AILIAMoAgQhACADQQA2AgQgAyAAIAEQMyIARQRAIAFBAWohAQwbCyADQRw2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIM8wILIAMoAgQhACADQQA2AgQgAyAAIAEQMyIARQRAIAFBAWohAQzrAgsgA0EdNgIcIAMgADYCDCADIAFBAWo2AhRBACECDPICCyAAQTtHDQEgAUEBaiEBC0EmIQIM1wILQQAhAiADQQA2AhwgAyABNgIUIANBnxU2AhAgA0EMNgIMDO8CCyABIARHBEADQCABLQAAQSBHDYQCIAQgAUEBaiIBRw0AC0EsIQIM7wILQSwhAgzuAgsgASAERgRAQTQhAgzuAgsCQAJAA0ACQCABLQAAQQprDgQCAAADAAsgBCABQQFqIgFHDQALQTQhAgzvAgsgAygCBCEAIANBADYCBCADIAAgARAxIgBFDZ8CIANBMjYCHCADIAE2AhQgAyAANgIMQQAhAgzuAgsgAygCBCEAIANBADYCBCADIAAgARAxIgBFBEAgAUEBaiEBDJ8CCyADQTI2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIM7QILIAEgBEcEQAJAA0AgAS0AAEEwayIAQf8BcUEKTwRAQTohAgzXAgsgAykDICILQpmz5syZs+bMGVYNASADIAtCCn4iCjcDICAKIACtQv8BgyILQn+FVg0BIAMgCiALfDcDICAEIAFBAWoiAUcNAAtBwAAhAgzuAgsgAygCBCEAIANBADYCBCADIAAgAUEBaiIBEDEiAA0XDOICC0HAACECDOwCCyABIARGBEBByQAhAgzsAgsCQANAAkAgAS0AAEEJaw4YAAKiAqICqQKiAqICogKiAqICogKiAqICogKiAqICogKiAqICogKiAqICogIAogILIAQgAUEBaiIBRw0AC0HJACECDOwCCyABQQFqIQEgA0Evai0AAEEBcQ2lAiADQQA2AhwgAyABNgIUIANBlxA2AhAgA0EKNgIMQQAhAgzrAgsgASAERwRAA0AgAS0AAEEgRw0VIAQgAUEBaiIBRw0AC0H4ACECDOsCC0H4ACECDOoCCyADQQI6ACgMOAtBACECIANBADYCHCADQb8LNgIQIANBAjYCDCADIAFBAWo2AhQM6AILQQAhAgzOAgtBDSECDM0CC0ETIQIMzAILQRUhAgzLAgtBFiECDMoCC0EYIQIMyQILQRkhAgzIAgtBGiECDMcCC0EbIQIMxgILQRwhAgzFAgtBHSECDMQCC0EeIQIMwwILQR8hAgzCAgtBICECDMECC0EiIQIMwAILQSMhAgy/AgtBJSECDL4CC0HlACECDL0CCyADQT02AhwgAyABNgIUIAMgADYCDEEAIQIM1QILIANBGzYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDNQCCyADQSA2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzTAgsgA0ETNgIcIAMgATYCFCADQZgaNgIQIANBFTYCDEEAIQIM0gILIANBCzYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNECCyADQRA2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzQAgsgA0EgNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIMzwILIANBCzYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDM4CCyADQQw2AhwgAyABNgIUIANBpBw2AhAgA0EVNgIMQQAhAgzNAgtBACECIANBADYCHCADIAE2AhQgA0HdDjYCECADQRI2AgwMzAILAkADQAJAIAEtAABBCmsOBAACAgACCyAEIAFBAWoiAUcNAAtB/QEhAgzMAgsCQAJAIAMtADZBAUcNAEEAIQACQCADKAI4IgJFDQAgAigCYCICRQ0AIAMgAhEAACEACyAARQ0AIABBFUcNASADQfwBNgIcIAMgATYCFCADQdwZNgIQIANBFTYCDEEAIQIMzQILQdwBIQIMswILIANBADYCHCADIAE2AhQgA0H5CzYCECADQR82AgxBACECDMsCCwJAAkAgAy0AKEEBaw4CBAEAC0HbASECDLICC0HUASECDLECCyADQQI6ADFBACEAAkAgAygCOCICRQ0AIAIoAgAiAkUNACADIAIRAAAhAAsgAEUEQEHdASECDLECCyAAQRVHBEAgA0EANgIcIAMgATYCFCADQbQMNgIQIANBEDYCDEEAIQIMygILIANB+wE2AhwgAyABNgIUIANBgRo2AhAgA0EVNgIMQQAhAgzJAgsgASAERgRAQfoBIQIMyQILIAEtAABByABGDQEgA0EBOgAoC0HAASECDK4CC0HaASECDK0CCyABIARHBEAgA0EMNgIIIAMgATYCBEHZASECDK0CC0H5ASECDMUCCyABIARGBEBB+AEhAgzFAgsgAS0AAEHIAEcNBCABQQFqIQFB2AEhAgyrAgsgASAERgRAQfcBIQIMxAILAkACQCABLQAAQcUAaw4QAAUFBQUFBQUFBQUFBQUFAQULIAFBAWohAUHWASECDKsCCyABQQFqIQFB1wEhAgyqAgtB9gEhAiABIARGDcICIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbrVAGotAABHDQMgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADMMCCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQLiIARQRAQeMBIQIMqgILIANB9QE2AhwgAyABNgIUIAMgADYCDEEAIQIMwgILQfQBIQIgASAERg3BAiADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEG41QBqLQAARw0CIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzCAgsgA0GBBDsBKCADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQLiIADQMMAgsgA0EANgIAC0EAIQIgA0EANgIcIAMgATYCFCADQeUfNgIQIANBCDYCDAy/AgtB1QEhAgylAgsgA0HzATYCHCADIAE2AhQgAyAANgIMQQAhAgy9AgtBACEAAkAgAygCOCICRQ0AIAIoAkAiAkUNACADIAIRAAAhAAsgAEUNbiAAQRVHBEAgA0EANgIcIAMgATYCFCADQYIPNgIQIANBIDYCDEEAIQIMvQILIANBjwE2AhwgAyABNgIUIANB7Bs2AhAgA0EVNgIMQQAhAgy8AgsgASAERwRAIANBDTYCCCADIAE2AgRB0wEhAgyjAgtB8gEhAgy7AgsgASAERgRAQfEBIQIMuwILAkACQAJAIAEtAABByABrDgsAAQgICAgICAgIAggLIAFBAWohAUHQASECDKMCCyABQQFqIQFB0QEhAgyiAgsgAUEBaiEBQdIBIQIMoQILQfABIQIgASAERg25AiADKAIAIgAgBCABa2ohBiABIABrQQJqIQUDQCABLQAAIABBtdUAai0AAEcNBCAAQQJGDQMgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMuQILQe8BIQIgASAERg24AiADKAIAIgAgBCABa2ohBiABIABrQQFqIQUDQCABLQAAIABBs9UAai0AAEcNAyAAQQFGDQIgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMuAILQe4BIQIgASAERg23AiADKAIAIgAgBCABa2ohBiABIABrQQJqIQUDQCABLQAAIABBsNUAai0AAEcNAiAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAY2AgAMtwILIAMoAgQhACADQgA3AwAgAyAAIAVBAWoiARArIgBFDQIgA0HsATYCHCADIAE2AhQgAyAANgIMQQAhAgy2AgsgA0EANgIACyADKAIEIQAgA0EANgIEIAMgACABECsiAEUNnAIgA0HtATYCHCADIAE2AhQgAyAANgIMQQAhAgy0AgtBzwEhAgyaAgtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDLQCC0HOASECDJoCCyADQesBNgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMsgILIAEgBEYEQEHrASECDLICCyABLQAAQS9GBEAgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GyODYCECADQQg2AgxBACECDLECC0HNASECDJcCCyABIARHBEAgA0EONgIIIAMgATYCBEHMASECDJcCC0HqASECDK8CCyABIARGBEBB6QEhAgyvAgsgAS0AAEEwayIAQf8BcUEKSQRAIAMgADoAKiABQQFqIQFBywEhAgyWAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZcCIANB6AE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILIAEgBEYEQEHnASECDK4CCwJAIAEtAABBLkYEQCABQQFqIQEMAQsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZgCIANB5gE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILQcoBIQIMlAILIAEgBEYEQEHlASECDK0CC0EAIQBBASEFQQEhB0EAIQICQAJAAkACQAJAAn8CQAJAAkACQAJAAkACQCABLQAAQTBrDgoKCQABAgMEBQYICwtBAgwGC0EDDAULQQQMBAtBBQwDC0EGDAILQQcMAQtBCAshAkEAIQVBACEHDAILQQkhAkEBIQBBACEFQQAhBwwBC0EAIQVBASECCyADIAI6ACsgAUEBaiEBAkACQCADLQAuQRBxDQACQAJAAkAgAy0AKg4DAQACBAsgB0UNAwwCCyAADQEMAgsgBUUNAQsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDQIgA0HiATYCHCADIAE2AhQgAyAANgIMQQAhAgyvAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZoCIANB4wE2AhwgAyABNgIUIAMgADYCDEEAIQIMrgILIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ2YAiADQeQBNgIcIAMgATYCFCADIAA2AgwMrQILQckBIQIMkwILQQAhAAJAIAMoAjgiAkUNACACKAJEIgJFDQAgAyACEQAAIQALAkAgAARAIABBFUYNASADQQA2AhwgAyABNgIUIANBpA02AhAgA0EhNgIMQQAhAgytAgtByAEhAgyTAgsgA0HhATYCHCADIAE2AhQgA0HQGjYCECADQRU2AgxBACECDKsCCyABIARGBEBB4QEhAgyrAgsCQCABLQAAQSBGBEAgA0EAOwE0IAFBAWohAQwBCyADQQA2AhwgAyABNgIUIANBmRE2AhAgA0EJNgIMQQAhAgyrAgtBxwEhAgyRAgsgASAERgRAQeABIQIMqgILAkAgAS0AAEEwa0H/AXEiAkEKSQRAIAFBAWohAQJAIAMvATQiAEGZM0sNACADIABBCmwiADsBNCAAQf7/A3EgAkH//wNzSw0AIAMgACACajsBNAwCC0EAIQIgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDAyrAgsgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDEEAIQIMqgILQcYBIQIMkAILIAEgBEYEQEHfASECDKkCCwJAIAEtAABBMGtB/wFxIgJBCkkEQCABQQFqIQECQCADLwE0IgBBmTNLDQAgAyAAQQpsIgA7ATQgAEH+/wNxIAJB//8Dc0sNACADIAAgAmo7ATQMAgtBACECIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgwMqgILIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgxBACECDKkCC0HFASECDI8CCyABIARGBEBB3gEhAgyoAgsCQCABLQAAQTBrQf8BcSICQQpJBEAgAUEBaiEBAkAgAy8BNCIAQZkzSw0AIAMgAEEKbCIAOwE0IABB/v8DcSACQf//A3NLDQAgAyAAIAJqOwE0DAILQQAhAiADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMDKkCCyADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMQQAhAgyoAgtBxAEhAgyOAgsgASAERgRAQd0BIQIMpwILAkACQAJAAkAgAS0AAEEKaw4XAgMDAAMDAwMDAwMDAwMDAwMDAwMDAwEDCyABQQFqDAULIAFBAWohAUHDASECDI8CCyABQQFqIQEgA0Evai0AAEEBcQ0IIANBADYCHCADIAE2AhQgA0GNCzYCECADQQ02AgxBACECDKcCCyADQQA2AhwgAyABNgIUIANBjQs2AhAgA0ENNgIMQQAhAgymAgsgASAERwRAIANBDzYCCCADIAE2AgRBASECDI0CC0HcASECDKUCCwJAAkADQAJAIAEtAABBCmsOBAIAAAMACyAEIAFBAWoiAUcNAAtB2wEhAgymAgsgAygCBCEAIANBADYCBCADIAAgARAtIgBFBEAgAUEBaiEBDAQLIANB2gE2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMpQILIAMoAgQhACADQQA2AgQgAyAAIAEQLSIADQEgAUEBagshAUHBASECDIoCCyADQdkBNgIcIAMgADYCDCADIAFBAWo2AhRBACECDKICC0HCASECDIgCCyADQS9qLQAAQQFxDQEgA0EANgIcIAMgATYCFCADQeQcNgIQIANBGTYCDEEAIQIMoAILIAEgBEYEQEHZASECDKACCwJAAkACQCABLQAAQQprDgQBAgIAAgsgAUEBaiEBDAILIAFBAWohAQwBCyADLQAuQcAAcUUNAQtBACEAAkAgAygCOCICRQ0AIAIoAjwiAkUNACADIAIRAAAhAAsgAEUNoAEgAEEVRgRAIANB2QA2AhwgAyABNgIUIANBtxo2AhAgA0EVNgIMQQAhAgyfAgsgA0EANgIcIAMgATYCFCADQYANNgIQIANBGzYCDEEAIQIMngILIANBADYCHCADIAE2AhQgA0HcKDYCECADQQI2AgxBACECDJ0CCyABIARHBEAgA0EMNgIIIAMgATYCBEG/ASECDIQCC0HYASECDJwCCyABIARGBEBB1wEhAgycAgsCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEtAABBwQBrDhUAAQIDWgQFBlpaWgcICQoLDA0ODxBaCyABQQFqIQFB+wAhAgySAgsgAUEBaiEBQfwAIQIMkQILIAFBAWohAUGBASECDJACCyABQQFqIQFBhQEhAgyPAgsgAUEBaiEBQYYBIQIMjgILIAFBAWohAUGJASECDI0CCyABQQFqIQFBigEhAgyMAgsgAUEBaiEBQY0BIQIMiwILIAFBAWohAUGWASECDIoCCyABQQFqIQFBlwEhAgyJAgsgAUEBaiEBQZgBIQIMiAILIAFBAWohAUGlASECDIcCCyABQQFqIQFBpgEhAgyGAgsgAUEBaiEBQawBIQIMhQILIAFBAWohAUG0ASECDIQCCyABQQFqIQFBtwEhAgyDAgsgAUEBaiEBQb4BIQIMggILIAEgBEYEQEHWASECDJsCCyABLQAAQc4ARw1IIAFBAWohAUG9ASECDIECCyABIARGBEBB1QEhAgyaAgsCQAJAAkAgAS0AAEHCAGsOEgBKSkpKSkpKSkoBSkpKSkpKAkoLIAFBAWohAUG4ASECDIICCyABQQFqIQFBuwEhAgyBAgsgAUEBaiEBQbwBIQIMgAILQdQBIQIgASAERg2YAiADKAIAIgAgBCABa2ohBSABIABrQQdqIQYCQANAIAEtAAAgAEGo1QBqLQAARw1FIABBB0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyZAgsgA0EANgIAIAZBAWohAUEbDEULIAEgBEYEQEHTASECDJgCCwJAAkAgAS0AAEHJAGsOBwBHR0dHRwFHCyABQQFqIQFBuQEhAgz/AQsgAUEBaiEBQboBIQIM/gELQdIBIQIgASAERg2WAiADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGm1QBqLQAARw1DIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyXAgsgA0EANgIAIAZBAWohAUEPDEMLQdEBIQIgASAERg2VAiADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGk1QBqLQAARw1CIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyWAgsgA0EANgIAIAZBAWohAUEgDEILQdABIQIgASAERg2UAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGh1QBqLQAARw1BIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyVAgsgA0EANgIAIAZBAWohAUESDEELIAEgBEYEQEHPASECDJQCCwJAAkAgAS0AAEHFAGsODgBDQ0NDQ0NDQ0NDQ0MBQwsgAUEBaiEBQbUBIQIM+wELIAFBAWohAUG2ASECDPoBC0HOASECIAEgBEYNkgIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBntUAai0AAEcNPyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMkwILIANBADYCACAGQQFqIQFBBww/C0HNASECIAEgBEYNkQIgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABBmNUAai0AAEcNPiAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMkgILIANBADYCACAGQQFqIQFBKAw+CyABIARGBEBBzAEhAgyRAgsCQAJAAkAgAS0AAEHFAGsOEQBBQUFBQUFBQUEBQUFBQUECQQsgAUEBaiEBQbEBIQIM+QELIAFBAWohAUGyASECDPgBCyABQQFqIQFBswEhAgz3AQtBywEhAiABIARGDY8CIAMoAgAiACAEIAFraiEFIAEgAGtBBmohBgJAA0AgAS0AACAAQZHVAGotAABHDTwgAEEGRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJACCyADQQA2AgAgBkEBaiEBQRoMPAtBygEhAiABIARGDY4CIAMoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQY3VAGotAABHDTsgAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADI8CCyADQQA2AgAgBkEBaiEBQSEMOwsgASAERgRAQckBIQIMjgILAkACQCABLQAAQcEAaw4UAD09PT09PT09PT09PT09PT09PQE9CyABQQFqIQFBrQEhAgz1AQsgAUEBaiEBQbABIQIM9AELIAEgBEYEQEHIASECDI0CCwJAAkAgAS0AAEHVAGsOCwA8PDw8PDw8PDwBPAsgAUEBaiEBQa4BIQIM9AELIAFBAWohAUGvASECDPMBC0HHASECIAEgBEYNiwIgAygCACIAIAQgAWtqIQUgASAAa0EIaiEGAkADQCABLQAAIABBhNUAai0AAEcNOCAAQQhGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMjAILIANBADYCACAGQQFqIQFBKgw4CyABIARGBEBBxgEhAgyLAgsgAS0AAEHQAEcNOCABQQFqIQFBJQw3C0HFASECIAEgBEYNiQIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBgdUAai0AAEcNNiAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMigILIANBADYCACAGQQFqIQFBDgw2CyABIARGBEBBxAEhAgyJAgsgAS0AAEHFAEcNNiABQQFqIQFBqwEhAgzvAQsgASAERgRAQcMBIQIMiAILAkACQAJAAkAgAS0AAEHCAGsODwABAjk5OTk5OTk5OTk5AzkLIAFBAWohAUGnASECDPEBCyABQQFqIQFBqAEhAgzwAQsgAUEBaiEBQakBIQIM7wELIAFBAWohAUGqASECDO4BC0HCASECIAEgBEYNhgIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABB/tQAai0AAEcNMyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMhwILIANBADYCACAGQQFqIQFBFAwzC0HBASECIAEgBEYNhQIgAygCACIAIAQgAWtqIQUgASAAa0EEaiEGAkADQCABLQAAIABB+dQAai0AAEcNMiAAQQRGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMhgILIANBADYCACAGQQFqIQFBKwwyC0HAASECIAEgBEYNhAIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABB9tQAai0AAEcNMSAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMhQILIANBADYCACAGQQFqIQFBLAwxC0G/ASECIAEgBEYNgwIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBodUAai0AAEcNMCAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMhAILIANBADYCACAGQQFqIQFBEQwwC0G+ASECIAEgBEYNggIgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABB8tQAai0AAEcNLyAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMgwILIANBADYCACAGQQFqIQFBLgwvCyABIARGBEBBvQEhAgyCAgsCQAJAAkACQAJAIAEtAABBwQBrDhUANDQ0NDQ0NDQ0NAE0NAI0NAM0NAQ0CyABQQFqIQFBmwEhAgzsAQsgAUEBaiEBQZwBIQIM6wELIAFBAWohAUGdASECDOoBCyABQQFqIQFBogEhAgzpAQsgAUEBaiEBQaQBIQIM6AELIAEgBEYEQEG8ASECDIECCwJAAkAgAS0AAEHSAGsOAwAwATALIAFBAWohAUGjASECDOgBCyABQQFqIQFBBAwtC0G7ASECIAEgBEYN/wEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABB8NQAai0AAEcNLCAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMgAILIANBADYCACAGQQFqIQFBHQwsCyABIARGBEBBugEhAgz/AQsCQAJAIAEtAABByQBrDgcBLi4uLi4ALgsgAUEBaiEBQaEBIQIM5gELIAFBAWohAUEiDCsLIAEgBEYEQEG5ASECDP4BCyABLQAAQdAARw0rIAFBAWohAUGgASECDOQBCyABIARGBEBBuAEhAgz9AQsCQAJAIAEtAABBxgBrDgsALCwsLCwsLCwsASwLIAFBAWohAUGeASECDOQBCyABQQFqIQFBnwEhAgzjAQtBtwEhAiABIARGDfsBIAMoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQezUAGotAABHDSggAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPwBCyADQQA2AgAgBkEBaiEBQQ0MKAtBtgEhAiABIARGDfoBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQaHVAGotAABHDScgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPsBCyADQQA2AgAgBkEBaiEBQQwMJwtBtQEhAiABIARGDfkBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQerUAGotAABHDSYgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPoBCyADQQA2AgAgBkEBaiEBQQMMJgtBtAEhAiABIARGDfgBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQejUAGotAABHDSUgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPkBCyADQQA2AgAgBkEBaiEBQSYMJQsgASAERgRAQbMBIQIM+AELAkACQCABLQAAQdQAaw4CAAEnCyABQQFqIQFBmQEhAgzfAQsgAUEBaiEBQZoBIQIM3gELQbIBIQIgASAERg32ASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHm1ABqLQAARw0jIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz3AQsgA0EANgIAIAZBAWohAUEnDCMLQbEBIQIgASAERg31ASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHk1ABqLQAARw0iIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz2AQsgA0EANgIAIAZBAWohAUEcDCILQbABIQIgASAERg30ASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEHe1ABqLQAARw0hIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz1AQsgA0EANgIAIAZBAWohAUEGDCELQa8BIQIgASAERg3zASADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANAIAEtAAAgAEHZ1ABqLQAARw0gIABBBEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAz0AQsgA0EANgIAIAZBAWohAUEZDCALIAEgBEYEQEGuASECDPMBCwJAAkACQAJAIAEtAABBLWsOIwAkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJAEkJCQkJAIkJCQDJAsgAUEBaiEBQY4BIQIM3AELIAFBAWohAUGPASECDNsBCyABQQFqIQFBlAEhAgzaAQsgAUEBaiEBQZUBIQIM2QELQa0BIQIgASAERg3xASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHX1ABqLQAARw0eIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzyAQsgA0EANgIAIAZBAWohAUELDB4LIAEgBEYEQEGsASECDPEBCwJAAkAgAS0AAEHBAGsOAwAgASALIAFBAWohAUGQASECDNgBCyABQQFqIQFBkwEhAgzXAQsgASAERgRAQasBIQIM8AELAkACQCABLQAAQcEAaw4PAB8fHx8fHx8fHx8fHx8BHwsgAUEBaiEBQZEBIQIM1wELIAFBAWohAUGSASECDNYBCyABIARGBEBBqgEhAgzvAQsgAS0AAEHMAEcNHCABQQFqIQFBCgwbC0GpASECIAEgBEYN7QEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABB0dQAai0AAEcNGiAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7gELIANBADYCACAGQQFqIQFBHgwaC0GoASECIAEgBEYN7AEgAygCACIAIAQgAWtqIQUgASAAa0EGaiEGAkADQCABLQAAIABBytQAai0AAEcNGSAAQQZGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7QELIANBADYCACAGQQFqIQFBFQwZC0GnASECIAEgBEYN6wEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBx9QAai0AAEcNGCAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM7AELIANBADYCACAGQQFqIQFBFwwYC0GmASECIAEgBEYN6gEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABBwdQAai0AAEcNFyAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6wELIANBADYCACAGQQFqIQFBGAwXCyABIARGBEBBpQEhAgzqAQsCQAJAIAEtAABByQBrDgcAGRkZGRkBGQsgAUEBaiEBQYsBIQIM0QELIAFBAWohAUGMASECDNABC0GkASECIAEgBEYN6AEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBptUAai0AAEcNFSAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6QELIANBADYCACAGQQFqIQFBCQwVC0GjASECIAEgBEYN5wEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBpNUAai0AAEcNFCAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM6AELIANBADYCACAGQQFqIQFBHwwUC0GiASECIAEgBEYN5gEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBvtQAai0AAEcNEyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM5wELIANBADYCACAGQQFqIQFBAgwTC0GhASECIAEgBEYN5QEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGA0AgAS0AACAAQbzUAGotAABHDREgAEEBRg0CIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADOUBCyABIARGBEBBoAEhAgzlAQtBASABLQAAQd8ARw0RGiABQQFqIQFBhwEhAgzLAQsgA0EANgIAIAZBAWohAUGIASECDMoBC0GfASECIAEgBEYN4gEgAygCACIAIAQgAWtqIQUgASAAa0EIaiEGAkADQCABLQAAIABBhNUAai0AAEcNDyAAQQhGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM4wELIANBADYCACAGQQFqIQFBKQwPC0GeASECIAEgBEYN4QEgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABBuNQAai0AAEcNDiAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM4gELIANBADYCACAGQQFqIQFBLQwOCyABIARGBEBBnQEhAgzhAQsgAS0AAEHFAEcNDiABQQFqIQFBhAEhAgzHAQsgASAERgRAQZwBIQIM4AELAkACQCABLQAAQcwAaw4IAA8PDw8PDwEPCyABQQFqIQFBggEhAgzHAQsgAUEBaiEBQYMBIQIMxgELQZsBIQIgASAERg3eASADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANAIAEtAAAgAEGz1ABqLQAARw0LIABBBEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzfAQsgA0EANgIAIAZBAWohAUEjDAsLQZoBIQIgASAERg3dASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGw1ABqLQAARw0KIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzeAQsgA0EANgIAIAZBAWohAUEADAoLIAEgBEYEQEGZASECDN0BCwJAAkAgAS0AAEHIAGsOCAAMDAwMDAwBDAsgAUEBaiEBQf0AIQIMxAELIAFBAWohAUGAASECDMMBCyABIARGBEBBmAEhAgzcAQsCQAJAIAEtAABBzgBrDgMACwELCyABQQFqIQFB/gAhAgzDAQsgAUEBaiEBQf8AIQIMwgELIAEgBEYEQEGXASECDNsBCyABLQAAQdkARw0IIAFBAWohAUEIDAcLQZYBIQIgASAERg3ZASADKAIAIgAgBCABa2ohBSABIABrQQNqIQYCQANAIAEtAAAgAEGs1ABqLQAARw0GIABBA0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzaAQsgA0EANgIAIAZBAWohAUEFDAYLQZUBIQIgASAERg3YASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEGm1ABqLQAARw0FIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzZAQsgA0EANgIAIAZBAWohAUEWDAULQZQBIQIgASAERg3XASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGh1QBqLQAARw0EIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzYAQsgA0EANgIAIAZBAWohAUEQDAQLIAEgBEYEQEGTASECDNcBCwJAAkAgAS0AAEHDAGsODAAGBgYGBgYGBgYGAQYLIAFBAWohAUH5ACECDL4BCyABQQFqIQFB+gAhAgy9AQtBkgEhAiABIARGDdUBIAMoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQaDUAGotAABHDQIgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNYBCyADQQA2AgAgBkEBaiEBQSQMAgsgA0EANgIADAILIAEgBEYEQEGRASECDNQBCyABLQAAQcwARw0BIAFBAWohAUETCzoAKSADKAIEIQAgA0EANgIEIAMgACABEC4iAA0CDAELQQAhAiADQQA2AhwgAyABNgIUIANB/h82AhAgA0EGNgIMDNEBC0H4ACECDLcBCyADQZABNgIcIAMgATYCFCADIAA2AgxBACECDM8BC0EAIQACQCADKAI4IgJFDQAgAigCQCICRQ0AIAMgAhEAACEACyAARQ0AIABBFUYNASADQQA2AhwgAyABNgIUIANBgg82AhAgA0EgNgIMQQAhAgzOAQtB9wAhAgy0AQsgA0GPATYCHCADIAE2AhQgA0HsGzYCECADQRU2AgxBACECDMwBCyABIARGBEBBjwEhAgzMAQsCQCABLQAAQSBGBEAgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GbHzYCECADQQY2AgxBACECDMwBC0ECIQIMsgELA0AgAS0AAEEgRw0CIAQgAUEBaiIBRw0AC0GOASECDMoBCyABIARGBEBBjQEhAgzKAQsCQCABLQAAQQlrDgRKAABKAAtB9QAhAgywAQsgAy0AKUEFRgRAQfYAIQIMsAELQfQAIQIMrwELIAEgBEYEQEGMASECDMgBCyADQRA2AgggAyABNgIEDAoLIAEgBEYEQEGLASECDMcBCwJAIAEtAABBCWsOBEcAAEcAC0HzACECDK0BCyABIARHBEAgA0EQNgIIIAMgATYCBEHxACECDK0BC0GKASECDMUBCwJAIAEgBEcEQANAIAEtAABBoNAAai0AACIAQQNHBEACQCAAQQFrDgJJAAQLQfAAIQIMrwELIAQgAUEBaiIBRw0AC0GIASECDMYBC0GIASECDMUBCyADQQA2AhwgAyABNgIUIANB2yA2AhAgA0EHNgIMQQAhAgzEAQsgASAERgRAQYkBIQIMxAELAkACQAJAIAEtAABBoNIAai0AAEEBaw4DRgIAAQtB8gAhAgysAQsgA0EANgIcIAMgATYCFCADQbQSNgIQIANBBzYCDEEAIQIMxAELQeoAIQIMqgELIAEgBEcEQCABQQFqIQFB7wAhAgyqAQtBhwEhAgzCAQsgBCABIgBGBEBBhgEhAgzCAQsgAC0AACIBQS9GBEAgAEEBaiEBQe4AIQIMqQELIAFBCWsiAkEXSw0BIAAhAUEBIAJ0QZuAgARxDUEMAQsgBCABIgBGBEBBhQEhAgzBAQsgAC0AAEEvRw0AIABBAWohAQwDC0EAIQIgA0EANgIcIAMgADYCFCADQdsgNgIQIANBBzYCDAy/AQsCQAJAAkACQAJAA0AgAS0AAEGgzgBqLQAAIgBBBUcEQAJAAkAgAEEBaw4IRwUGBwgABAEIC0HrACECDK0BCyABQQFqIQFB7QAhAgysAQsgBCABQQFqIgFHDQALQYQBIQIMwwELIAFBAWoMFAsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDR4gA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgzBAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDR4gA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgzAAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDR4gA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgy/AQsgA0EANgIcIAMgATYCFCADQfkPNgIQIANBBzYCDEEAIQIMvgELIAEgBEYEQEGDASECDL4BCwJAIAEtAABBoM4Aai0AAEEBaw4IPgQFBgAIAgMHCyABQQFqIQELQQMhAgyjAQsgAUEBagwNC0EAIQIgA0EANgIcIANB0RI2AhAgA0EHNgIMIAMgAUEBajYCFAy6AQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDRYgA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgy5AQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDRYgA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgy4AQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDRYgA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgy3AQsgA0EANgIcIAMgATYCFCADQfkPNgIQIANBBzYCDEEAIQIMtgELQewAIQIMnAELIAEgBEYEQEGCASECDLUBCyABQQFqDAILIAEgBEYEQEGBASECDLQBCyABQQFqDAELIAEgBEYNASABQQFqCyEBQQQhAgyYAQtBgAEhAgywAQsDQCABLQAAQaDMAGotAAAiAEECRwRAIABBAUcEQEHpACECDJkBCwwxCyAEIAFBAWoiAUcNAAtB/wAhAgyvAQsgASAERgRAQf4AIQIMrwELAkAgAS0AAEEJaw43LwMGLwQGBgYGBgYGBgYGBgYGBgYGBgYFBgYCBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGAAYLIAFBAWoLIQFBBSECDJQBCyABQQFqDAYLIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0IIANB2wA2AhwgAyABNgIUIAMgADYCDEEAIQIMqwELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0IIANB3QA2AhwgAyABNgIUIAMgADYCDEEAIQIMqgELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0IIANB+gA2AhwgAyABNgIUIAMgADYCDEEAIQIMqQELIANBADYCHCADIAE2AhQgA0GNFDYCECADQQc2AgxBACECDKgBCwJAAkACQAJAA0AgAS0AAEGgygBqLQAAIgBBBUcEQAJAIABBAWsOBi4DBAUGAAYLQegAIQIMlAELIAQgAUEBaiIBRw0AC0H9ACECDKsBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNByADQdsANgIcIAMgATYCFCADIAA2AgxBACECDKoBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNByADQd0ANgIcIAMgATYCFCADIAA2AgxBACECDKkBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNByADQfoANgIcIAMgATYCFCADIAA2AgxBACECDKgBCyADQQA2AhwgAyABNgIUIANB5Ag2AhAgA0EHNgIMQQAhAgynAQsgASAERg0BIAFBAWoLIQFBBiECDIwBC0H8ACECDKQBCwJAAkACQAJAA0AgAS0AAEGgyABqLQAAIgBBBUcEQCAAQQFrDgQpAgMEBQsgBCABQQFqIgFHDQALQfsAIQIMpwELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB2wA2AhwgAyABNgIUIAMgADYCDEEAIQIMpgELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB3QA2AhwgAyABNgIUIAMgADYCDEEAIQIMpQELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0DIANB+gA2AhwgAyABNgIUIAMgADYCDEEAIQIMpAELIANBADYCHCADIAE2AhQgA0G8CjYCECADQQc2AgxBACECDKMBC0HPACECDIkBC0HRACECDIgBC0HnACECDIcBCyABIARGBEBB+gAhAgygAQsCQCABLQAAQQlrDgQgAAAgAAsgAUEBaiEBQeYAIQIMhgELIAEgBEYEQEH5ACECDJ8BCwJAIAEtAABBCWsOBB8AAB8AC0EAIQACQCADKAI4IgJFDQAgAigCOCICRQ0AIAMgAhEAACEACyAARQRAQeIBIQIMhgELIABBFUcEQCADQQA2AhwgAyABNgIUIANByQ02AhAgA0EaNgIMQQAhAgyfAQsgA0H4ADYCHCADIAE2AhQgA0HqGjYCECADQRU2AgxBACECDJ4BCyABIARHBEAgA0ENNgIIIAMgATYCBEHkACECDIUBC0H3ACECDJ0BCyABIARGBEBB9gAhAgydAQsCQAJAAkAgAS0AAEHIAGsOCwABCwsLCwsLCwsCCwsgAUEBaiEBQd0AIQIMhQELIAFBAWohAUHgACECDIQBCyABQQFqIQFB4wAhAgyDAQtB9QAhAiABIARGDZsBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbXVAGotAABHDQggAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJwBCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQKyIABEAgA0H0ADYCHCADIAE2AhQgAyAANgIMQQAhAgycAQtB4gAhAgyCAQtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJwBC0HhACECDIIBCyADQfMANgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMmgELIAMtACkiAEEja0ELSQ0JAkAgAEEGSw0AQQEgAHRBygBxRQ0ADAoLQQAhAiADQQA2AhwgAyABNgIUIANB7Qk2AhAgA0EINgIMDJkBC0HyACECIAEgBEYNmAEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABBs9UAai0AAEcNBSAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMmQELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgAEQCADQfEANgIcIAMgATYCFCADIAA2AgxBACECDJkBC0HfACECDH8LQQAhAAJAIAMoAjgiAkUNACACKAI0IgJFDQAgAyACEQAAIQALAkAgAARAIABBFUYNASADQQA2AhwgAyABNgIUIANB6g02AhAgA0EmNgIMQQAhAgyZAQtB3gAhAgx/CyADQfAANgIcIAMgATYCFCADQYAbNgIQIANBFTYCDEEAIQIMlwELIAMtAClBIUYNBiADQQA2AhwgAyABNgIUIANBkQo2AhAgA0EINgIMQQAhAgyWAQtB7wAhAiABIARGDZUBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbDVAGotAABHDQIgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJYBCyADKAIEIQAgA0IANwMAIAMgACAGQQFqIgEQKyIARQ0CIANB7QA2AhwgAyABNgIUIAMgADYCDEEAIQIMlQELIANBADYCAAsgAygCBCEAIANBADYCBCADIAAgARArIgBFDYABIANB7gA2AhwgAyABNgIUIAMgADYCDEEAIQIMkwELQdwAIQIMeQtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJMBC0HbACECDHkLIANB7AA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyRAQsgAy0AKSIAQSNJDQAgAEEuRg0AIANBADYCHCADIAE2AhQgA0HJCTYCECADQQg2AgxBACECDJABC0HaACECDHYLIAEgBEYEQEHrACECDI8BCwJAIAEtAABBL0YEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDEEAIQIMjwELQdkAIQIMdQsgASAERwRAIANBDjYCCCADIAE2AgRB2AAhAgx1C0HqACECDI0BCyABIARGBEBB6QAhAgyNAQsgAS0AAEEwayIAQf8BcUEKSQRAIAMgADoAKiABQQFqIQFB1wAhAgx0CyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNeiADQegANgIcIAMgATYCFCADIAA2AgxBACECDIwBCyABIARGBEBB5wAhAgyMAQsCQCABLQAAQS5GBEAgAUEBaiEBDAELIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ17IANB5gA2AhwgAyABNgIUIAMgADYCDEEAIQIMjAELQdYAIQIMcgsgASAERgRAQeUAIQIMiwELQQAhAEEBIQVBASEHQQAhAgJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAIAEtAABBMGsOCgoJAAECAwQFBggLC0ECDAYLQQMMBQtBBAwEC0EFDAMLQQYMAgtBBwwBC0EICyECQQAhBUEAIQcMAgtBCSECQQEhAEEAIQVBACEHDAELQQAhBUEBIQILIAMgAjoAKyABQQFqIQECQAJAIAMtAC5BEHENAAJAAkACQCADLQAqDgMBAAIECyAHRQ0DDAILIAANAQwCCyAFRQ0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNAiADQeIANgIcIAMgATYCFCADIAA2AgxBACECDI0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNfSADQeMANgIcIAMgATYCFCADIAA2AgxBACECDIwBCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNeyADQeQANgIcIAMgATYCFCADIAA2AgwMiwELQdQAIQIMcQsgAy0AKUEiRg2GAUHTACECDHALQQAhAAJAIAMoAjgiAkUNACACKAJEIgJFDQAgAyACEQAAIQALIABFBEBB1QAhAgxwCyAAQRVHBEAgA0EANgIcIAMgATYCFCADQaQNNgIQIANBITYCDEEAIQIMiQELIANB4QA2AhwgAyABNgIUIANB0Bo2AhAgA0EVNgIMQQAhAgyIAQsgASAERgRAQeAAIQIMiAELAkACQAJAAkACQCABLQAAQQprDgQBBAQABAsgAUEBaiEBDAELIAFBAWohASADQS9qLQAAQQFxRQ0BC0HSACECDHALIANBADYCHCADIAE2AhQgA0G2ETYCECADQQk2AgxBACECDIgBCyADQQA2AhwgAyABNgIUIANBthE2AhAgA0EJNgIMQQAhAgyHAQsgASAERgRAQd8AIQIMhwELIAEtAABBCkYEQCABQQFqIQEMCQsgAy0ALkHAAHENCCADQQA2AhwgAyABNgIUIANBthE2AhAgA0ECNgIMQQAhAgyGAQsgASAERgRAQd0AIQIMhgELIAEtAAAiAkENRgRAIAFBAWohAUHQACECDG0LIAEhACACQQlrDgQFAQEFAQsgBCABIgBGBEBB3AAhAgyFAQsgAC0AAEEKRw0AIABBAWoMAgtBACECIANBADYCHCADIAA2AhQgA0HKLTYCECADQQc2AgwMgwELIAEgBEYEQEHbACECDIMBCwJAIAEtAABBCWsOBAMAAAMACyABQQFqCyEBQc4AIQIMaAsgASAERgRAQdoAIQIMgQELIAEtAABBCWsOBAABAQABC0EAIQIgA0EANgIcIANBmhI2AhAgA0EHNgIMIAMgAUEBajYCFAx/CyADQYASOwEqQQAhAAJAIAMoAjgiAkUNACACKAI4IgJFDQAgAyACEQAAIQALIABFDQAgAEEVRw0BIANB2QA2AhwgAyABNgIUIANB6ho2AhAgA0EVNgIMQQAhAgx+C0HNACECDGQLIANBADYCHCADIAE2AhQgA0HJDTYCECADQRo2AgxBACECDHwLIAEgBEYEQEHZACECDHwLIAEtAABBIEcNPSABQQFqIQEgAy0ALkEBcQ09IANBADYCHCADIAE2AhQgA0HCHDYCECADQR42AgxBACECDHsLIAEgBEYEQEHYACECDHsLAkACQAJAAkACQCABLQAAIgBBCmsOBAIDAwABCyABQQFqIQFBLCECDGULIABBOkcNASADQQA2AhwgAyABNgIUIANB5xE2AhAgA0EKNgIMQQAhAgx9CyABQQFqIQEgA0Evai0AAEEBcUUNcyADLQAyQYABcUUEQCADQTJqIQIgAxA1QQAhAAJAIAMoAjgiBkUNACAGKAIoIgZFDQAgAyAGEQAAIQALAkACQCAADhZNTEsBAQEBAQEBAQEBAQEBAQEBAQEAAQsgA0EpNgIcIAMgATYCFCADQawZNgIQIANBFTYCDEEAIQIMfgsgA0EANgIcIAMgATYCFCADQeULNgIQIANBETYCDEEAIQIMfQtBACEAAkAgAygCOCICRQ0AIAIoAlwiAkUNACADIAIRAAAhAAsgAEUNWSAAQRVHDQEgA0EFNgIcIAMgATYCFCADQZsbNgIQIANBFTYCDEEAIQIMfAtBywAhAgxiC0EAIQIgA0EANgIcIAMgATYCFCADQZAONgIQIANBFDYCDAx6CyADIAMvATJBgAFyOwEyDDsLIAEgBEcEQCADQRE2AgggAyABNgIEQcoAIQIMYAtB1wAhAgx4CyABIARGBEBB1gAhAgx4CwJAAkACQAJAIAEtAAAiAEEgciAAIABBwQBrQf8BcUEaSRtB/wFxQeMAaw4TAEBAQEBAQEBAQEBAQAFAQEACA0ALIAFBAWohAUHGACECDGELIAFBAWohAUHHACECDGALIAFBAWohAUHIACECDF8LIAFBAWohAUHJACECDF4LQdUAIQIgBCABIgBGDXYgBCABayADKAIAIgFqIQYgACABa0EFaiEHA0AgAUGQyABqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0IQQQgAUEFRg0KGiABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAx2C0HUACECIAQgASIARg11IAQgAWsgAygCACIBaiEGIAAgAWtBD2ohBwNAIAFBgMgAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNB0EDIAFBD0YNCRogAUEBaiEBIAQgAEEBaiIARw0ACyADIAY2AgAMdQtB0wAhAiAEIAEiAEYNdCAEIAFrIAMoAgAiAWohBiAAIAFrQQ5qIQcDQCABQeLHAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQYgAUEORg0HIAFBAWohASAEIABBAWoiAEcNAAsgAyAGNgIADHQLQdIAIQIgBCABIgBGDXMgBCABayADKAIAIgFqIQUgACABa0EBaiEGA0AgAUHgxwBqLQAAIAAtAAAiB0EgciAHIAdBwQBrQf8BcUEaSRtB/wFxRw0FIAFBAUYNAiABQQFqIQEgBCAAQQFqIgBHDQALIAMgBTYCAAxzCyABIARGBEBB0QAhAgxzCwJAAkAgAS0AACIAQSByIAAgAEHBAGtB/wFxQRpJG0H/AXFB7gBrDgcAOTk5OTkBOQsgAUEBaiEBQcMAIQIMWgsgAUEBaiEBQcQAIQIMWQsgA0EANgIAIAZBAWohAUHFACECDFgLQdAAIQIgBCABIgBGDXAgBCABayADKAIAIgFqIQYgACABa0EJaiEHA0AgAUHWxwBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0CQQIgAUEJRg0EGiABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAxwC0HPACECIAQgASIARg1vIAQgAWsgAygCACIBaiEGIAAgAWtBBWohBwNAIAFB0McAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNASABQQVGDQIgAUEBaiEBIAQgAEEBaiIARw0ACyADIAY2AgAMbwsgACEBIANBADYCAAwzC0EBCzoALCADQQA2AgAgB0EBaiEBC0EtIQIMUgsCQANAIAEtAABB0MUAai0AAEEBRw0BIAQgAUEBaiIBRw0AC0HNACECDGsLQcIAIQIMUQsgASAERgRAQcwAIQIMagsgAS0AAEE6RgRAIAMoAgQhACADQQA2AgQgAyAAIAEQMCIARQ0zIANBywA2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMagsgA0EANgIcIAMgATYCFCADQecRNgIQIANBCjYCDEEAIQIMaQsCQAJAIAMtACxBAmsOAgABJwsgA0Ezai0AAEECcUUNJiADLQAuQQJxDSYgA0EANgIcIAMgATYCFCADQaYUNgIQIANBCzYCDEEAIQIMaQsgAy0AMkEgcUUNJSADLQAuQQJxDSUgA0EANgIcIAMgATYCFCADQb0TNgIQIANBDzYCDEEAIQIMaAtBACEAAkAgAygCOCICRQ0AIAIoAkgiAkUNACADIAIRAAAhAAsgAEUEQEHBACECDE8LIABBFUcEQCADQQA2AhwgAyABNgIUIANBpg82AhAgA0EcNgIMQQAhAgxoCyADQcoANgIcIAMgATYCFCADQYUcNgIQIANBFTYCDEEAIQIMZwsgASAERwRAA0AgAS0AAEHAwQBqLQAAQQFHDRcgBCABQQFqIgFHDQALQcQAIQIMZwtBxAAhAgxmCyABIARHBEADQAJAIAEtAAAiAEEgciAAIABBwQBrQf8BcUEaSRtB/wFxIgBBCUYNACAAQSBGDQACQAJAAkACQCAAQeMAaw4TAAMDAwMDAwMBAwMDAwMDAwMDAgMLIAFBAWohAUE2IQIMUgsgAUEBaiEBQTchAgxRCyABQQFqIQFBOCECDFALDBULIAQgAUEBaiIBRw0AC0E8IQIMZgtBPCECDGULIAEgBEYEQEHIACECDGULIANBEjYCCCADIAE2AgQCQAJAAkACQAJAIAMtACxBAWsOBBQAAQIJCyADLQAyQSBxDQNB4AEhAgxPCwJAIAMvATIiAEEIcUUNACADLQAoQQFHDQAgAy0ALkEIcUUNAgsgAyAAQff7A3FBgARyOwEyDAsLIAMgAy8BMkEQcjsBMgwECyADQQA2AgQgAyABIAEQMSIABEAgA0HBADYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgxmCyABQQFqIQEMWAsgA0EANgIcIAMgATYCFCADQfQTNgIQIANBBDYCDEEAIQIMZAtBxwAhAiABIARGDWMgAygCACIAIAQgAWtqIQUgASAAa0EGaiEGAkADQCAAQcDFAGotAAAgAS0AAEEgckcNASAAQQZGDUogAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMZAsgA0EANgIADAULAkAgASAERwRAA0AgAS0AAEHAwwBqLQAAIgBBAUcEQCAAQQJHDQMgAUEBaiEBDAULIAQgAUEBaiIBRw0AC0HFACECDGQLQcUAIQIMYwsLIANBADoALAwBC0ELIQIMRwtBPyECDEYLAkACQANAIAEtAAAiAEEgRwRAAkAgAEEKaw4EAwUFAwALIABBLEYNAwwECyAEIAFBAWoiAUcNAAtBxgAhAgxgCyADQQg6ACwMDgsgAy0AKEEBRw0CIAMtAC5BCHENAiADKAIEIQAgA0EANgIEIAMgACABEDEiAARAIANBwgA2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMXwsgAUEBaiEBDFALQTshAgxECwJAA0AgAS0AACIAQSBHIABBCUdxDQEgBCABQQFqIgFHDQALQcMAIQIMXQsLQTwhAgxCCwJAAkAgASAERwRAA0AgAS0AACIAQSBHBEAgAEEKaw4EAwQEAwQLIAQgAUEBaiIBRw0AC0E/IQIMXQtBPyECDFwLIAMgAy8BMkEgcjsBMgwKCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUNTiADQT42AhwgAyABNgIUIAMgADYCDEEAIQIMWgsCQCABIARHBEADQCABLQAAQcDDAGotAAAiAEEBRwRAIABBAkYNAwwMCyAEIAFBAWoiAUcNAAtBNyECDFsLQTchAgxaCyABQQFqIQEMBAtBOyECIAQgASIARg1YIAQgAWsgAygCACIBaiEGIAAgAWtBBWohBwJAA0AgAUGQyABqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0BIAFBBUYEQEEHIQEMPwsgAUEBaiEBIAQgAEEBaiIARw0ACyADIAY2AgAMWQsgA0EANgIAIAAhAQwFC0E6IQIgBCABIgBGDVcgBCABayADKAIAIgFqIQYgACABa0EIaiEHAkADQCABQbTBAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQEgAUEIRgRAQQUhAQw+CyABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAxYCyADQQA2AgAgACEBDAQLQTkhAiAEIAEiAEYNViAEIAFrIAMoAgAiAWohBiAAIAFrQQNqIQcCQANAIAFBsMEAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNASABQQNGBEBBBiEBDD0LIAFBAWohASAEIABBAWoiAEcNAAsgAyAGNgIADFcLIANBADYCACAAIQEMAwsCQANAIAEtAAAiAEEgRwRAIABBCmsOBAcEBAcCCyAEIAFBAWoiAUcNAAtBOCECDFYLIABBLEcNASABQQFqIQBBASEBAkACQAJAAkACQCADLQAsQQVrDgQDAQIEAAsgACEBDAQLQQIhAQwBC0EEIQELIANBAToALCADIAMvATIgAXI7ATIgACEBDAELIAMgAy8BMkEIcjsBMiAAIQELQT4hAgw7CyADQQA6ACwLQTkhAgw5CyABIARGBEBBNiECDFILAkACQAJAAkACQCABLQAAQQprDgQAAgIBAgsgAygCBCEAIANBADYCBCADIAAgARAxIgBFDQIgA0EzNgIcIAMgATYCFCADIAA2AgxBACECDFULIAMoAgQhACADQQA2AgQgAyAAIAEQMSIARQRAIAFBAWohAQwGCyADQTI2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMVAsgAy0ALkEBcQRAQd8BIQIMOwsgAygCBCEAIANBADYCBCADIAAgARAxIgANAQxJC0E0IQIMOQsgA0E1NgIcIAMgATYCFCADIAA2AgxBACECDFELQTUhAgw3CyADQS9qLQAAQQFxDQAgA0EANgIcIAMgATYCFCADQesWNgIQIANBGTYCDEEAIQIMTwtBMyECDDULIAEgBEYEQEEyIQIMTgsCQCABLQAAQQpGBEAgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GSFzYCECADQQM2AgxBACECDE4LQTIhAgw0CyABIARGBEBBMSECDE0LAkAgAS0AACIAQQlGDQAgAEEgRg0AQQEhAgJAIAMtACxBBWsOBAYEBQANCyADIAMvATJBCHI7ATIMDAsgAy0ALkEBcUUNASADLQAsQQhHDQAgA0EAOgAsC0E9IQIMMgsgA0EANgIcIAMgATYCFCADQcIWNgIQIANBCjYCDEEAIQIMSgtBAiECDAELQQQhAgsgA0EBOgAsIAMgAy8BMiACcjsBMgwGCyABIARGBEBBMCECDEcLIAEtAABBCkYEQCABQQFqIQEMAQsgAy0ALkEBcQ0AIANBADYCHCADIAE2AhQgA0HcKDYCECADQQI2AgxBACECDEYLQTAhAgwsCyABQQFqIQFBMSECDCsLIAEgBEYEQEEvIQIMRAsgAS0AACIAQQlHIABBIEdxRQRAIAFBAWohASADLQAuQQFxDQEgA0EANgIcIAMgATYCFCADQZcQNgIQIANBCjYCDEEAIQIMRAtBASECAkACQAJAAkACQAJAIAMtACxBAmsOBwUEBAMBAgAECyADIAMvATJBCHI7ATIMAwtBAiECDAELQQQhAgsgA0EBOgAsIAMgAy8BMiACcjsBMgtBLyECDCsLIANBADYCHCADIAE2AhQgA0GEEzYCECADQQs2AgxBACECDEMLQeEBIQIMKQsgASAERgRAQS4hAgxCCyADQQA2AgQgA0ESNgIIIAMgASABEDEiAA0BC0EuIQIMJwsgA0EtNgIcIAMgATYCFCADIAA2AgxBACECDD8LQQAhAAJAIAMoAjgiAkUNACACKAJMIgJFDQAgAyACEQAAIQALIABFDQAgAEEVRw0BIANB2AA2AhwgAyABNgIUIANBsxs2AhAgA0EVNgIMQQAhAgw+C0HMACECDCQLIANBADYCHCADIAE2AhQgA0GzDjYCECADQR02AgxBACECDDwLIAEgBEYEQEHOACECDDwLIAEtAAAiAEEgRg0CIABBOkYNAQsgA0EAOgAsQQkhAgwhCyADKAIEIQAgA0EANgIEIAMgACABEDAiAA0BDAILIAMtAC5BAXEEQEHeASECDCALIAMoAgQhACADQQA2AgQgAyAAIAEQMCIARQ0CIANBKjYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgw4CyADQcsANgIcIAMgADYCDCADIAFBAWo2AhRBACECDDcLIAFBAWohAUHAACECDB0LIAFBAWohAQwsCyABIARGBEBBKyECDDULAkAgAS0AAEEKRgRAIAFBAWohAQwBCyADLQAuQcAAcUUNBgsgAy0AMkGAAXEEQEEAIQACQCADKAI4IgJFDQAgAigCXCICRQ0AIAMgAhEAACEACyAARQ0SIABBFUYEQCADQQU2AhwgAyABNgIUIANBmxs2AhAgA0EVNgIMQQAhAgw2CyADQQA2AhwgAyABNgIUIANBkA42AhAgA0EUNgIMQQAhAgw1CyADQTJqIQIgAxA1QQAhAAJAIAMoAjgiBkUNACAGKAIoIgZFDQAgAyAGEQAAIQALIAAOFgIBAAQEBAQEBAQEBAQEBAQEBAQEBAMECyADQQE6ADALIAIgAi8BAEHAAHI7AQALQSshAgwYCyADQSk2AhwgAyABNgIUIANBrBk2AhAgA0EVNgIMQQAhAgwwCyADQQA2AhwgAyABNgIUIANB5Qs2AhAgA0ERNgIMQQAhAgwvCyADQQA2AhwgAyABNgIUIANBpQs2AhAgA0ECNgIMQQAhAgwuC0EBIQcgAy8BMiIFQQhxRQRAIAMpAyBCAFIhBwsCQCADLQAwBEBBASEAIAMtAClBBUYNASAFQcAAcUUgB3FFDQELAkAgAy0AKCICQQJGBEBBASEAIAMvATQiBkHlAEYNAkEAIQAgBUHAAHENAiAGQeQARg0CIAZB5gBrQQJJDQIgBkHMAUYNAiAGQbACRg0CDAELQQAhACAFQcAAcQ0BC0ECIQAgBUEIcQ0AIAVBgARxBEACQCACQQFHDQAgAy0ALkEKcQ0AQQUhAAwCC0EEIQAMAQsgBUEgcUUEQCADEDZBAEdBAnQhAAwBC0EAQQMgAykDIFAbIQALIABBAWsOBQIABwEDBAtBESECDBMLIANBAToAMQwpC0EAIQICQCADKAI4IgBFDQAgACgCMCIARQ0AIAMgABEAACECCyACRQ0mIAJBFUYEQCADQQM2AhwgAyABNgIUIANB0hs2AhAgA0EVNgIMQQAhAgwrC0EAIQIgA0EANgIcIAMgATYCFCADQd0ONgIQIANBEjYCDAwqCyADQQA2AhwgAyABNgIUIANB+SA2AhAgA0EPNgIMQQAhAgwpC0EAIQACQCADKAI4IgJFDQAgAigCMCICRQ0AIAMgAhEAACEACyAADQELQQ4hAgwOCyAAQRVGBEAgA0ECNgIcIAMgATYCFCADQdIbNgIQIANBFTYCDEEAIQIMJwsgA0EANgIcIAMgATYCFCADQd0ONgIQIANBEjYCDEEAIQIMJgtBKiECDAwLIAEgBEcEQCADQQk2AgggAyABNgIEQSkhAgwMC0EmIQIMJAsgAyADKQMgIgwgBCABa60iCn0iC0IAIAsgDFgbNwMgIAogDFQEQEElIQIMJAsgAygCBCEAIANBADYCBCADIAAgASAMp2oiARAyIgBFDQAgA0EFNgIcIAMgATYCFCADIAA2AgxBACECDCMLQQ8hAgwJC0IAIQoCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAS0AAEEwaw43FxYAAQIDBAUGBxQUFBQUFBQICQoLDA0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFA4PEBESExQLQgIhCgwWC0IDIQoMFQtCBCEKDBQLQgUhCgwTC0IGIQoMEgtCByEKDBELQgghCgwQC0IJIQoMDwtCCiEKDA4LQgshCgwNC0IMIQoMDAtCDSEKDAsLQg4hCgwKC0IPIQoMCQtCCiEKDAgLQgshCgwHC0IMIQoMBgtCDSEKDAULQg4hCgwEC0IPIQoMAwsgA0EANgIcIAMgATYCFCADQZ8VNgIQIANBDDYCDEEAIQIMIQsgASAERgRAQSIhAgwhC0IAIQoCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEtAABBMGsONxUUAAECAwQFBgcWFhYWFhYWCAkKCwwNFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYODxAREhMWC0ICIQoMFAtCAyEKDBMLQgQhCgwSC0IFIQoMEQtCBiEKDBALQgchCgwPC0IIIQoMDgtCCSEKDA0LQgohCgwMC0ILIQoMCwtCDCEKDAoLQg0hCgwJC0IOIQoMCAtCDyEKDAcLQgohCgwGC0ILIQoMBQtCDCEKDAQLQg0hCgwDC0IOIQoMAgtCDyEKDAELQgEhCgsgAUEBaiEBIAMpAyAiC0L//////////w9YBEAgAyALQgSGIAqENwMgDAILIANBADYCHCADIAE2AhQgA0G1CTYCECADQQw2AgxBACECDB4LQSchAgwEC0EoIQIMAwsgAyABOgAsIANBADYCACAHQQFqIQFBDCECDAILIANBADYCACAGQQFqIQFBCiECDAELIAFBAWohAUEIIQIMAAsAC0EAIQIgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDAwXC0EAIQIgA0EANgIcIAMgATYCFCADQYMRNgIQIANBCTYCDAwWC0EAIQIgA0EANgIcIAMgATYCFCADQd8KNgIQIANBCTYCDAwVC0EAIQIgA0EANgIcIAMgATYCFCADQe0QNgIQIANBCTYCDAwUC0EAIQIgA0EANgIcIAMgATYCFCADQdIRNgIQIANBCTYCDAwTC0EAIQIgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDAwSC0EAIQIgA0EANgIcIAMgATYCFCADQYMRNgIQIANBCTYCDAwRC0EAIQIgA0EANgIcIAMgATYCFCADQd8KNgIQIANBCTYCDAwQC0EAIQIgA0EANgIcIAMgATYCFCADQe0QNgIQIANBCTYCDAwPC0EAIQIgA0EANgIcIAMgATYCFCADQdIRNgIQIANBCTYCDAwOC0EAIQIgA0EANgIcIAMgATYCFCADQbkXNgIQIANBDzYCDAwNC0EAIQIgA0EANgIcIAMgATYCFCADQbkXNgIQIANBDzYCDAwMC0EAIQIgA0EANgIcIAMgATYCFCADQZkTNgIQIANBCzYCDAwLC0EAIQIgA0EANgIcIAMgATYCFCADQZ0JNgIQIANBCzYCDAwKC0EAIQIgA0EANgIcIAMgATYCFCADQZcQNgIQIANBCjYCDAwJC0EAIQIgA0EANgIcIAMgATYCFCADQbEQNgIQIANBCjYCDAwIC0EAIQIgA0EANgIcIAMgATYCFCADQbsdNgIQIANBAjYCDAwHC0EAIQIgA0EANgIcIAMgATYCFCADQZYWNgIQIANBAjYCDAwGC0EAIQIgA0EANgIcIAMgATYCFCADQfkYNgIQIANBAjYCDAwFC0EAIQIgA0EANgIcIAMgATYCFCADQcQYNgIQIANBAjYCDAwECyADQQI2AhwgAyABNgIUIANBqR42AhAgA0EWNgIMQQAhAgwDC0HeACECIAEgBEYNAiAJQQhqIQcgAygCACEFAkACQCABIARHBEAgBUGWyABqIQggBCAFaiABayEGIAVBf3NBCmoiBSABaiEAA0AgAS0AACAILQAARwRAQQIhCAwDCyAFRQRAQQAhCCAAIQEMAwsgBUEBayEFIAhBAWohCCAEIAFBAWoiAUcNAAsgBiEFIAQhAQsgB0EBNgIAIAMgBTYCAAwBCyADQQA2AgAgByAINgIACyAHIAE2AgQgCSgCDCEAAkACQCAJKAIIQQFrDgIEAQALIANBADYCHCADQcIeNgIQIANBFzYCDCADIABBAWo2AhRBACECDAMLIANBADYCHCADIAA2AhQgA0HXHjYCECADQQk2AgxBACECDAILIAEgBEYEQEEoIQIMAgsgA0EJNgIIIAMgATYCBEEnIQIMAQsgASAERgRAQQEhAgwBCwNAAkACQAJAIAEtAABBCmsOBAABAQABCyABQQFqIQEMAQsgAUEBaiEBIAMtAC5BIHENAEEAIQIgA0EANgIcIAMgATYCFCADQaEhNgIQIANBBTYCDAwCC0EBIQIgASAERw0ACwsgCUEQaiQAIAJFBEAgAygCDCEADAELIAMgAjYCHEEAIQAgAygCBCIBRQ0AIAMgASAEIAMoAggRAQAiAUUNACADIAQ2AhQgAyABNgIMIAEhAAsgAAu+AgECfyAAQQA6AAAgAEHkAGoiAUEBa0EAOgAAIABBADoAAiAAQQA6AAEgAUEDa0EAOgAAIAFBAmtBADoAACAAQQA6AAMgAUEEa0EAOgAAQQAgAGtBA3EiASAAaiIAQQA2AgBB5AAgAWtBfHEiAiAAaiIBQQRrQQA2AgACQCACQQlJDQAgAEEANgIIIABBADYCBCABQQhrQQA2AgAgAUEMa0EANgIAIAJBGUkNACAAQQA2AhggAEEANgIUIABBADYCECAAQQA2AgwgAUEQa0EANgIAIAFBFGtBADYCACABQRhrQQA2AgAgAUEca0EANgIAIAIgAEEEcUEYciICayIBQSBJDQAgACACaiEAA0AgAEIANwMYIABCADcDECAAQgA3AwggAEIANwMAIABBIGohACABQSBrIgFBH0sNAAsLC1YBAX8CQCAAKAIMDQACQAJAAkACQCAALQAxDgMBAAMCCyAAKAI4IgFFDQAgASgCMCIBRQ0AIAAgAREAACIBDQMLQQAPCwALIABByhk2AhBBDiEBCyABCxoAIAAoAgxFBEAgAEHeHzYCECAAQRU2AgwLCxQAIAAoAgxBFUYEQCAAQQA2AgwLCxQAIAAoAgxBFkYEQCAAQQA2AgwLCwcAIAAoAgwLBwAgACgCEAsJACAAIAE2AhALBwAgACgCFAsrAAJAIABBJ08NAEL//////wkgAK2IQgGDUA0AIABBAnRB0DhqKAIADwsACxcAIABBL08EQAALIABBAnRB7DlqKAIAC78JAQF/QfQtIQECQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAQeQAaw70A2NiAAFhYWFhYWECAwQFYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQYHCAkKCwwNDg9hYWFhYRBhYWFhYWFhYWFhYRFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWESExQVFhcYGRobYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYRwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1NmE3ODk6YWFhYWFhYWE7YWFhPGFhYWE9Pj9hYWFhYWFhYUBhYUFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFCQ0RFRkdISUpLTE1OT1BRUlNhYWFhYWFhYVRVVldYWVpbYVxdYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhXmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYV9gYQtB6iwPC0GYJg8LQe0xDwtBoDcPC0HJKQ8LQbQpDwtBli0PC0HrKw8LQaI1DwtB2zQPC0HgKQ8LQeMkDwtB1SQPC0HuJA8LQeYlDwtByjQPC0HQNw8LQao1DwtB9SwPC0H2Jg8LQYIiDwtB8jMPC0G+KA8LQec3DwtBzSEPC0HAIQ8LQbglDwtByyUPC0GWJA8LQY80DwtBzTUPC0HdKg8LQe4zDwtBnDQPC0GeMQ8LQfQ1DwtB5SIPC0GvJQ8LQZkxDwtBsjYPC0H5Ng8LQcQyDwtB3SwPC0GCMQ8LQcExDwtBjTcPC0HJJA8LQew2DwtB5yoPC0HIIw8LQeIhDwtByTcPC0GlIg8LQZQiDwtB2zYPC0HeNQ8LQYYmDwtBvCsPC0GLMg8LQaAjDwtB9jAPC0GALA8LQYkrDwtBpCYPC0HyIw8LQYEoDwtBqzIPC0HrJw8LQcI2DwtBoiQPC0HPKg8LQdwjDwtBhycPC0HkNA8LQbciDwtBrTEPC0HVIg8LQa80DwtB3iYPC0HWMg8LQfQ0DwtBgTgPC0H0Nw8LQZI2DwtBnScPC0GCKQ8LQY0jDwtB1zEPC0G9NQ8LQbQ3DwtB2DAPC0G2Jw8LQZo4DwtBpyoPC0HEJw8LQa4jDwtB9SIPCwALQcomIQELIAELFwAgACAALwEuQf7/A3EgAUEAR3I7AS4LGgAgACAALwEuQf3/A3EgAUEAR0EBdHI7AS4LGgAgACAALwEuQfv/A3EgAUEAR0ECdHI7AS4LGgAgACAALwEuQff/A3EgAUEAR0EDdHI7AS4LGgAgACAALwEuQe//A3EgAUEAR0EEdHI7AS4LGgAgACAALwEuQd//A3EgAUEAR0EFdHI7AS4LGgAgACAALwEuQb//A3EgAUEAR0EGdHI7AS4LGgAgACAALwEuQf/+A3EgAUEAR0EHdHI7AS4LGgAgACAALwEuQf/9A3EgAUEAR0EIdHI7AS4LGgAgACAALwEuQf/7A3EgAUEAR0EJdHI7AS4LPgECfwJAIAAoAjgiA0UNACADKAIEIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEHhEjYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIIIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEH8ETYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIMIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEHsCjYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIQIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEH6HjYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIUIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEHLEDYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIYIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEG3HzYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIcIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEG/FTYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIsIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEH+CDYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIgIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEGMHTYCEEEYIQQLIAQLPgECfwJAIAAoAjgiA0UNACADKAIkIgNFDQAgACABIAIgAWsgAxEBACIEQX9HDQAgAEHmFTYCEEEYIQQLIAQLOAAgAAJ/IAAvATJBFHFBFEYEQEEBIAAtAChBAUYNARogAC8BNEHlAEYMAQsgAC0AKUEFRgs6ADALWQECfwJAIAAtAChBAUYNACAALwE0IgFB5ABrQeQASQ0AIAFBzAFGDQAgAUGwAkYNACAALwEyIgBBwABxDQBBASECIABBiARxQYAERg0AIABBKHFFIQILIAILjAEBAn8CQAJAAkAgAC0AKkUNACAALQArRQ0AIAAvATIiAUECcUUNAQwCCyAALwEyIgFBAXFFDQELQQEhAiAALQAoQQFGDQAgAC8BNCIAQeQAa0HkAEkNACAAQcwBRg0AIABBsAJGDQAgAUHAAHENAEEAIQIgAUGIBHFBgARGDQAgAUEocUEARyECCyACC1cAIABBGGpCADcDACAAQgA3AwAgAEE4akIANwMAIABBMGpCADcDACAAQShqQgA3AwAgAEEgakIANwMAIABBEGpCADcDACAAQQhqQgA3AwAgAEH9ATYCHAsGACAAEDoLmi0BC38jAEEQayIKJABB3NUAKAIAIglFBEBBnNkAKAIAIgVFBEBBqNkAQn83AgBBoNkAQoCAhICAgMAANwIAQZzZACAKQQhqQXBxQdiq1aoFcyIFNgIAQbDZAEEANgIAQYDZAEEANgIAC0GE2QBBwNkENgIAQdTVAEHA2QQ2AgBB6NUAIAU2AgBB5NUAQX82AgBBiNkAQcCmAzYCAANAIAFBgNYAaiABQfTVAGoiAjYCACACIAFB7NUAaiIDNgIAIAFB+NUAaiADNgIAIAFBiNYAaiABQfzVAGoiAzYCACADIAI2AgAgAUGQ1gBqIAFBhNYAaiICNgIAIAIgAzYCACABQYzWAGogAjYCACABQSBqIgFBgAJHDQALQczZBEGBpgM2AgBB4NUAQazZACgCADYCAEHQ1QBBgKYDNgIAQdzVAEHI2QQ2AgBBzP8HQTg2AgBByNkEIQkLAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAEHsAU0EQEHE1QAoAgAiBkEQIABBE2pBcHEgAEELSRsiBEEDdiIAdiIBQQNxBEACQCABQQFxIAByQQFzIgJBA3QiAEHs1QBqIgEgAEH01QBqKAIAIgAoAggiA0YEQEHE1QAgBkF+IAJ3cTYCAAwBCyABIAM2AgggAyABNgIMCyAAQQhqIQEgACACQQN0IgJBA3I2AgQgACACaiIAIAAoAgRBAXI2AgQMEQtBzNUAKAIAIgggBE8NASABBEACQEECIAB0IgJBACACa3IgASAAdHFoIgBBA3QiAkHs1QBqIgEgAkH01QBqKAIAIgIoAggiA0YEQEHE1QAgBkF+IAB3cSIGNgIADAELIAEgAzYCCCADIAE2AgwLIAIgBEEDcjYCBCAAQQN0IgAgBGshBSAAIAJqIAU2AgAgAiAEaiIEIAVBAXI2AgQgCARAIAhBeHFB7NUAaiEAQdjVACgCACEDAn9BASAIQQN2dCIBIAZxRQRAQcTVACABIAZyNgIAIAAMAQsgACgCCAsiASADNgIMIAAgAzYCCCADIAA2AgwgAyABNgIICyACQQhqIQFB2NUAIAQ2AgBBzNUAIAU2AgAMEQtByNUAKAIAIgtFDQEgC2hBAnRB9NcAaigCACIAKAIEQXhxIARrIQUgACECA0ACQCACKAIQIgFFBEAgAkEUaigCACIBRQ0BCyABKAIEQXhxIARrIgMgBUkhAiADIAUgAhshBSABIAAgAhshACABIQIMAQsLIAAoAhghCSAAKAIMIgMgAEcEQEHU1QAoAgAaIAMgACgCCCIBNgIIIAEgAzYCDAwQCyAAQRRqIgIoAgAiAUUEQCAAKAIQIgFFDQMgAEEQaiECCwNAIAIhByABIgNBFGoiAigCACIBDQAgA0EQaiECIAMoAhAiAQ0ACyAHQQA2AgAMDwtBfyEEIABBv39LDQAgAEETaiIBQXBxIQRByNUAKAIAIghFDQBBACAEayEFAkACQAJAAn9BACAEQYACSQ0AGkEfIARB////B0sNABogBEEmIAFBCHZnIgBrdkEBcSAAQQF0a0E+agsiBkECdEH01wBqKAIAIgJFBEBBACEBQQAhAwwBC0EAIQEgBEEZIAZBAXZrQQAgBkEfRxt0IQBBACEDA0ACQCACKAIEQXhxIARrIgcgBU8NACACIQMgByIFDQBBACEFIAIhAQwDCyABIAJBFGooAgAiByAHIAIgAEEddkEEcWpBEGooAgAiAkYbIAEgBxshASAAQQF0IQAgAg0ACwsgASADckUEQEEAIQNBAiAGdCIAQQAgAGtyIAhxIgBFDQMgAGhBAnRB9NcAaigCACEBCyABRQ0BCwNAIAEoAgRBeHEgBGsiAiAFSSEAIAIgBSAAGyEFIAEgAyAAGyEDIAEoAhAiAAR/IAAFIAFBFGooAgALIgENAAsLIANFDQAgBUHM1QAoAgAgBGtPDQAgAygCGCEHIAMgAygCDCIARwRAQdTVACgCABogACADKAIIIgE2AgggASAANgIMDA4LIANBFGoiAigCACIBRQRAIAMoAhAiAUUNAyADQRBqIQILA0AgAiEGIAEiAEEUaiICKAIAIgENACAAQRBqIQIgACgCECIBDQALIAZBADYCAAwNC0HM1QAoAgAiAyAETwRAQdjVACgCACEBAkAgAyAEayICQRBPBEAgASAEaiIAIAJBAXI2AgQgASADaiACNgIAIAEgBEEDcjYCBAwBCyABIANBA3I2AgQgASADaiIAIAAoAgRBAXI2AgRBACEAQQAhAgtBzNUAIAI2AgBB2NUAIAA2AgAgAUEIaiEBDA8LQdDVACgCACIDIARLBEAgBCAJaiIAIAMgBGsiAUEBcjYCBEHc1QAgADYCAEHQ1QAgATYCACAJIARBA3I2AgQgCUEIaiEBDA8LQQAhASAEAn9BnNkAKAIABEBBpNkAKAIADAELQajZAEJ/NwIAQaDZAEKAgISAgIDAADcCAEGc2QAgCkEMakFwcUHYqtWqBXM2AgBBsNkAQQA2AgBBgNkAQQA2AgBBgIAECyIAIARBxwBqIgVqIgZBACAAayIHcSICTwRAQbTZAEEwNgIADA8LAkBB/NgAKAIAIgFFDQBB9NgAKAIAIgggAmohACAAIAFNIAAgCEtxDQBBACEBQbTZAEEwNgIADA8LQYDZAC0AAEEEcQ0EAkACQCAJBEBBhNkAIQEDQCABKAIAIgAgCU0EQCAAIAEoAgRqIAlLDQMLIAEoAggiAQ0ACwtBABA7IgBBf0YNBSACIQZBoNkAKAIAIgFBAWsiAyAAcQRAIAIgAGsgACADakEAIAFrcWohBgsgBCAGTw0FIAZB/v///wdLDQVB/NgAKAIAIgMEQEH02AAoAgAiByAGaiEBIAEgB00NBiABIANLDQYLIAYQOyIBIABHDQEMBwsgBiADayAHcSIGQf7///8HSw0EIAYQOyEAIAAgASgCACABKAIEakYNAyAAIQELAkAgBiAEQcgAak8NACABQX9GDQBBpNkAKAIAIgAgBSAGa2pBACAAa3EiAEH+////B0sEQCABIQAMBwsgABA7QX9HBEAgACAGaiEGIAEhAAwHC0EAIAZrEDsaDAQLIAEiAEF/Rw0FDAMLQQAhAwwMC0EAIQAMCgsgAEF/Rw0CC0GA2QBBgNkAKAIAQQRyNgIACyACQf7///8HSw0BIAIQOyEAQQAQOyEBIABBf0YNASABQX9GDQEgACABTw0BIAEgAGsiBiAEQThqTQ0BC0H02ABB9NgAKAIAIAZqIgE2AgBB+NgAKAIAIAFJBEBB+NgAIAE2AgALAkACQAJAQdzVACgCACICBEBBhNkAIQEDQCAAIAEoAgAiAyABKAIEIgVqRg0CIAEoAggiAQ0ACwwCC0HU1QAoAgAiAUEARyAAIAFPcUUEQEHU1QAgADYCAAtBACEBQYjZACAGNgIAQYTZACAANgIAQeTVAEF/NgIAQejVAEGc2QAoAgA2AgBBkNkAQQA2AgADQCABQYDWAGogAUH01QBqIgI2AgAgAiABQezVAGoiAzYCACABQfjVAGogAzYCACABQYjWAGogAUH81QBqIgM2AgAgAyACNgIAIAFBkNYAaiABQYTWAGoiAjYCACACIAM2AgAgAUGM1gBqIAI2AgAgAUEgaiIBQYACRw0AC0F4IABrQQ9xIgEgAGoiAiAGQThrIgMgAWsiAUEBcjYCBEHg1QBBrNkAKAIANgIAQdDVACABNgIAQdzVACACNgIAIAAgA2pBODYCBAwCCyAAIAJNDQAgAiADSQ0AIAEoAgxBCHENAEF4IAJrQQ9xIgAgAmoiA0HQ1QAoAgAgBmoiByAAayIAQQFyNgIEIAEgBSAGajYCBEHg1QBBrNkAKAIANgIAQdDVACAANgIAQdzVACADNgIAIAIgB2pBODYCBAwBCyAAQdTVACgCAEkEQEHU1QAgADYCAAsgACAGaiEDQYTZACEBAkACQAJAA0AgAyABKAIARwRAIAEoAggiAQ0BDAILCyABLQAMQQhxRQ0BC0GE2QAhAQNAIAEoAgAiAyACTQRAIAMgASgCBGoiBSACSw0DCyABKAIIIQEMAAsACyABIAA2AgAgASABKAIEIAZqNgIEIABBeCAAa0EPcWoiCSAEQQNyNgIEIANBeCADa0EPcWoiBiAEIAlqIgRrIQEgAiAGRgRAQdzVACAENgIAQdDVAEHQ1QAoAgAgAWoiADYCACAEIABBAXI2AgQMCAtB2NUAKAIAIAZGBEBB2NUAIAQ2AgBBzNUAQczVACgCACABaiIANgIAIAQgAEEBcjYCBCAAIARqIAA2AgAMCAsgBigCBCIFQQNxQQFHDQYgBUF4cSEIIAVB/wFNBEAgBUEDdiEDIAYoAggiACAGKAIMIgJGBEBBxNUAQcTVACgCAEF+IAN3cTYCAAwHCyACIAA2AgggACACNgIMDAYLIAYoAhghByAGIAYoAgwiAEcEQCAAIAYoAggiAjYCCCACIAA2AgwMBQsgBkEUaiICKAIAIgVFBEAgBigCECIFRQ0EIAZBEGohAgsDQCACIQMgBSIAQRRqIgIoAgAiBQ0AIABBEGohAiAAKAIQIgUNAAsgA0EANgIADAQLQXggAGtBD3EiASAAaiIHIAZBOGsiAyABayIBQQFyNgIEIAAgA2pBODYCBCACIAVBNyAFa0EPcWpBP2siAyADIAJBEGpJGyIDQSM2AgRB4NUAQazZACgCADYCAEHQ1QAgATYCAEHc1QAgBzYCACADQRBqQYzZACkCADcCACADQYTZACkCADcCCEGM2QAgA0EIajYCAEGI2QAgBjYCAEGE2QAgADYCAEGQ2QBBADYCACADQSRqIQEDQCABQQc2AgAgBSABQQRqIgFLDQALIAIgA0YNACADIAMoAgRBfnE2AgQgAyADIAJrIgU2AgAgAiAFQQFyNgIEIAVB/wFNBEAgBUF4cUHs1QBqIQACf0HE1QAoAgAiAUEBIAVBA3Z0IgNxRQRAQcTVACABIANyNgIAIAAMAQsgACgCCAsiASACNgIMIAAgAjYCCCACIAA2AgwgAiABNgIIDAELQR8hASAFQf///wdNBEAgBUEmIAVBCHZnIgBrdkEBcSAAQQF0a0E+aiEBCyACIAE2AhwgAkIANwIQIAFBAnRB9NcAaiEAQcjVACgCACIDQQEgAXQiBnFFBEAgACACNgIAQcjVACADIAZyNgIAIAIgADYCGCACIAI2AgggAiACNgIMDAELIAVBGSABQQF2a0EAIAFBH0cbdCEBIAAoAgAhAwJAA0AgAyIAKAIEQXhxIAVGDQEgAUEddiEDIAFBAXQhASAAIANBBHFqQRBqIgYoAgAiAw0ACyAGIAI2AgAgAiAANgIYIAIgAjYCDCACIAI2AggMAQsgACgCCCIBIAI2AgwgACACNgIIIAJBADYCGCACIAA2AgwgAiABNgIIC0HQ1QAoAgAiASAETQ0AQdzVACgCACIAIARqIgIgASAEayIBQQFyNgIEQdDVACABNgIAQdzVACACNgIAIAAgBEEDcjYCBCAAQQhqIQEMCAtBACEBQbTZAEEwNgIADAcLQQAhAAsgB0UNAAJAIAYoAhwiAkECdEH01wBqIgMoAgAgBkYEQCADIAA2AgAgAA0BQcjVAEHI1QAoAgBBfiACd3E2AgAMAgsgB0EQQRQgBygCECAGRhtqIAA2AgAgAEUNAQsgACAHNgIYIAYoAhAiAgRAIAAgAjYCECACIAA2AhgLIAZBFGooAgAiAkUNACAAQRRqIAI2AgAgAiAANgIYCyABIAhqIQEgBiAIaiIGKAIEIQULIAYgBUF+cTYCBCABIARqIAE2AgAgBCABQQFyNgIEIAFB/wFNBEAgAUF4cUHs1QBqIQACf0HE1QAoAgAiAkEBIAFBA3Z0IgFxRQRAQcTVACABIAJyNgIAIAAMAQsgACgCCAsiASAENgIMIAAgBDYCCCAEIAA2AgwgBCABNgIIDAELQR8hBSABQf///wdNBEAgAUEmIAFBCHZnIgBrdkEBcSAAQQF0a0E+aiEFCyAEIAU2AhwgBEIANwIQIAVBAnRB9NcAaiEAQcjVACgCACICQQEgBXQiA3FFBEAgACAENgIAQcjVACACIANyNgIAIAQgADYCGCAEIAQ2AgggBCAENgIMDAELIAFBGSAFQQF2a0EAIAVBH0cbdCEFIAAoAgAhAAJAA0AgACICKAIEQXhxIAFGDQEgBUEddiEAIAVBAXQhBSACIABBBHFqQRBqIgMoAgAiAA0ACyADIAQ2AgAgBCACNgIYIAQgBDYCDCAEIAQ2AggMAQsgAigCCCIAIAQ2AgwgAiAENgIIIARBADYCGCAEIAI2AgwgBCAANgIICyAJQQhqIQEMAgsCQCAHRQ0AAkAgAygCHCIBQQJ0QfTXAGoiAigCACADRgRAIAIgADYCACAADQFByNUAIAhBfiABd3EiCDYCAAwCCyAHQRBBFCAHKAIQIANGG2ogADYCACAARQ0BCyAAIAc2AhggAygCECIBBEAgACABNgIQIAEgADYCGAsgA0EUaigCACIBRQ0AIABBFGogATYCACABIAA2AhgLAkAgBUEPTQRAIAMgBCAFaiIAQQNyNgIEIAAgA2oiACAAKAIEQQFyNgIEDAELIAMgBGoiAiAFQQFyNgIEIAMgBEEDcjYCBCACIAVqIAU2AgAgBUH/AU0EQCAFQXhxQezVAGohAAJ/QcTVACgCACIBQQEgBUEDdnQiBXFFBEBBxNUAIAEgBXI2AgAgAAwBCyAAKAIICyIBIAI2AgwgACACNgIIIAIgADYCDCACIAE2AggMAQtBHyEBIAVB////B00EQCAFQSYgBUEIdmciAGt2QQFxIABBAXRrQT5qIQELIAIgATYCHCACQgA3AhAgAUECdEH01wBqIQBBASABdCIEIAhxRQRAIAAgAjYCAEHI1QAgBCAIcjYCACACIAA2AhggAiACNgIIIAIgAjYCDAwBCyAFQRkgAUEBdmtBACABQR9HG3QhASAAKAIAIQQCQANAIAQiACgCBEF4cSAFRg0BIAFBHXYhBCABQQF0IQEgACAEQQRxakEQaiIGKAIAIgQNAAsgBiACNgIAIAIgADYCGCACIAI2AgwgAiACNgIIDAELIAAoAggiASACNgIMIAAgAjYCCCACQQA2AhggAiAANgIMIAIgATYCCAsgA0EIaiEBDAELAkAgCUUNAAJAIAAoAhwiAUECdEH01wBqIgIoAgAgAEYEQCACIAM2AgAgAw0BQcjVACALQX4gAXdxNgIADAILIAlBEEEUIAkoAhAgAEYbaiADNgIAIANFDQELIAMgCTYCGCAAKAIQIgEEQCADIAE2AhAgASADNgIYCyAAQRRqKAIAIgFFDQAgA0EUaiABNgIAIAEgAzYCGAsCQCAFQQ9NBEAgACAEIAVqIgFBA3I2AgQgACABaiIBIAEoAgRBAXI2AgQMAQsgACAEaiIHIAVBAXI2AgQgACAEQQNyNgIEIAUgB2ogBTYCACAIBEAgCEF4cUHs1QBqIQFB2NUAKAIAIQMCf0EBIAhBA3Z0IgIgBnFFBEBBxNUAIAIgBnI2AgAgAQwBCyABKAIICyICIAM2AgwgASADNgIIIAMgATYCDCADIAI2AggLQdjVACAHNgIAQczVACAFNgIACyAAQQhqIQELIApBEGokACABC0MAIABFBEA/AEEQdA8LAkAgAEH//wNxDQAgAEEASA0AIABBEHZAACIAQX9GBEBBtNkAQTA2AgBBfw8LIABBEHQPCwALC5lCIgBBgAgLDQEAAAAAAAAAAgAAAAMAQZgICwUEAAAABQBBqAgLCQYAAAAHAAAACABB5AgLwjJJbnZhbGlkIGNoYXIgaW4gdXJsIHF1ZXJ5AFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fYm9keQBDb250ZW50LUxlbmd0aCBvdmVyZmxvdwBDaHVuayBzaXplIG92ZXJmbG93AEludmFsaWQgbWV0aG9kIGZvciBIVFRQL3gueCByZXF1ZXN0AEludmFsaWQgbWV0aG9kIGZvciBSVFNQL3gueCByZXF1ZXN0AEV4cGVjdGVkIFNPVVJDRSBtZXRob2QgZm9yIElDRS94LnggcmVxdWVzdABJbnZhbGlkIGNoYXIgaW4gdXJsIGZyYWdtZW50IHN0YXJ0AEV4cGVjdGVkIGRvdABTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3N0YXR1cwBJbnZhbGlkIHJlc3BvbnNlIHN0YXR1cwBFeHBlY3RlZCBMRiBhZnRlciBoZWFkZXJzAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMAVXNlciBjYWxsYmFjayBlcnJvcgBgb25fcmVzZXRgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19oZWFkZXJgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3Byb3RvY29sX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fcHJvdG9jb2wARW1wdHkgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyYWN0ZXIgaW4gQ29udGVudC1MZW5ndGgAVHJhbnNmZXItRW5jb2RpbmcgY2FuJ3QgYmUgcHJlc2VudCB3aXRoIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgc2l6ZQBFeHBlY3RlZCBMRiBhZnRlciBjaHVuayBzaXplAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBVbmV4cGVjdGVkIHdoaXRlc3BhY2UgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgTEYgYWZ0ZXIgaGVhZGVyIHZhbHVlAEludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYCBoZWFkZXIgdmFsdWUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciBjaHVuayBleHRlbnNpb24gdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZSB2YWx1ZQBJbnZhbGlkIHF1b3RlZC1wYWlyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fcHJvdG9jb2xfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciByZXNwb25zZSBsaW5lAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBuYW1lAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgZXh0ZW5zaW9uIG5hbWUASW52YWxpZCBzdGF0dXMgY29kZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABNaXNzaW5nIGV4cGVjdGVkIENSIGFmdGVyIGNodW5rIGRhdGEARXhwZWN0ZWQgTEYgYWZ0ZXIgY2h1bmsgZGF0YQBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AARGF0YSBhZnRlciBgQ29ubmVjdGlvbjogY2xvc2VgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBRVUVSWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAEV4cGVjdGVkIExGIGFmdGVyIENSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX1BST1RPQ09MX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19DT01QTEVURQBIUEVfQ0JfSEVBREVSX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9OQU1FX0NPTVBMRVRFAEhQRV9DQl9NRVNTQUdFX0NPTVBMRVRFAEhQRV9DQl9NRVRIT0RfQ09NUExFVEUASFBFX0NCX0hFQURFUl9GSUVMRF9DT01QTEVURQBERUxFVEUASFBFX0lOVkFMSURfRU9GX1NUQVRFAElOVkFMSURfU1NMX0NFUlRJRklDQVRFAFBBVVNFAE5PX1JFU1BPTlNFAFVOU1VQUE9SVEVEX01FRElBX1RZUEUAR09ORQBOT1RfQUNDRVBUQUJMRQBTRVJWSUNFX1VOQVZBSUxBQkxFAFJBTkdFX05PVF9TQVRJU0ZJQUJMRQBPUklHSU5fSVNfVU5SRUFDSEFCTEUAUkVTUE9OU0VfSVNfU1RBTEUAUFVSR0UATUVSR0UAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRQBSRVFVRVNUX0hFQURFUl9UT09fTEFSR0UAUEFZTE9BRF9UT09fTEFSR0UASU5TVUZGSUNJRU5UX1NUT1JBR0UASFBFX1BBVVNFRF9VUEdSQURFAEhQRV9QQVVTRURfSDJfVVBHUkFERQBTT1VSQ0UAQU5OT1VOQ0UAVFJBQ0UASFBFX1VORVhQRUNURURfU1BBQ0UAREVTQ1JJQkUAVU5TVUJTQ1JJQkUAUkVDT1JEAEhQRV9JTlZBTElEX01FVEhPRABOT1RfRk9VTkQAUFJPUEZJTkQAVU5CSU5EAFJFQklORABVTkFVVEhPUklaRUQATUVUSE9EX05PVF9BTExPV0VEAEhUVFBfVkVSU0lPTl9OT1RfU1VQUE9SVEVEAEFMUkVBRFlfUkVQT1JURUQAQUNDRVBURUQATk9UX0lNUExFTUVOVEVEAExPT1BfREVURUNURUQASFBFX0NSX0VYUEVDVEVEAEhQRV9MRl9FWFBFQ1RFRABDUkVBVEVEAElNX1VTRUQASFBFX1BBVVNFRABUSU1FT1VUX09DQ1VSRUQAUEFZTUVOVF9SRVFVSVJFRABQUkVDT05ESVRJT05fUkVRVUlSRUQAUFJPWFlfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATkVUV09SS19BVVRIRU5USUNBVElPTl9SRVFVSVJFRABMRU5HVEhfUkVRVUlSRUQAU1NMX0NFUlRJRklDQVRFX1JFUVVJUkVEAFVQR1JBREVfUkVRVUlSRUQAUEFHRV9FWFBJUkVEAFBSRUNPTkRJVElPTl9GQUlMRUQARVhQRUNUQVRJT05fRkFJTEVEAFJFVkFMSURBVElPTl9GQUlMRUQAU1NMX0hBTkRTSEFLRV9GQUlMRUQATE9DS0VEAFRSQU5TRk9STUFUSU9OX0FQUExJRUQATk9UX01PRElGSUVEAE5PVF9FWFRFTkRFRABCQU5EV0lEVEhfTElNSVRfRVhDRUVERUQAU0lURV9JU19PVkVSTE9BREVEAEhFQUQARXhwZWN0ZWQgSFRUUC8sIFJUU1AvIG9yIElDRS8A5xUAAK8VAACkEgAAkhoAACYWAACeFAAA2xkAAHkVAAB+EgAA/hQAADYVAAALFgAA2BYAAPMSAABCGAAArBYAABIVAAAUFwAA7xcAAEgUAABxFwAAshoAAGsZAAB+GQAANRQAAIIaAABEFwAA/RYAAB4YAACHFwAAqhkAAJMSAAAHGAAALBcAAMoXAACkFwAA5xUAAOcVAABYFwAAOxgAAKASAAAtHAAAwxEAAEgRAADeEgAAQhMAAKQZAAD9EAAA9xUAAKUVAADvFgAA+BkAAEoWAABWFgAA9RUAAAoaAAAIGgAAARoAAKsVAABCEgAA1xAAAEwRAAAFGQAAVBYAAB4RAADKGQAAyBkAAE4WAAD/GAAAcRQAAPAVAADuFQAAlBkAAPwVAAC/GQAAmxkAAHwUAABDEQAAcBgAAJUUAAAnFAAAGRQAANUSAADUGQAARBYAAPcQAEG5OwsBAQBB0DsL4AEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBuj0LBAEAAAIAQdE9C14DBAMDAwMDAAADAwADAwADAwMDAwMDAwMDAAUAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAwADAEG6PwsEAQAAAgBB0T8LXgMAAwMDAwMAAAMDAAMDAAMDAwMDAwMDAwMABAAFAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwADAAMAQbDBAAsNbG9zZWVlcC1hbGl2ZQBBycEACwEBAEHgwQAL4AEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBycMACwEBAEHgwwAL5wEBAQEBAQEBAQEBAQECAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAWNodW5rZWQAQfHFAAteAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQBB0McACyFlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AQYDIAAsgcmFuc2Zlci1lbmNvZGluZ3BncmFkZQ0KDQpTTQ0KDQoAQanIAAsFAQIAAQMAQcDIAAtfBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanKAAsFAQIAAQMAQcDKAAtfBAUFBgUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanMAAsEAQAAAQBBwcwAC14CAgACAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAEGpzgALBQECAAEDAEHAzgALXwQFAAAFBQUFBQUFBQUFBQYFBQUFBQUFBQUFBQUABQAHCAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQAFAAUABQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAAAAFAEGp0AALBQEBAAEBAEHA0AALAQEAQdrQAAtBAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQanSAAsFAQEAAQEAQcDSAAsBAQBBytIACwYCAAAAAAIAQeHSAAs6AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBoNQAC50BTk9VTkNFRUNLT1VUTkVDVEVURUNSSUJFTFVTSEVURUFEU0VBUkNIUkdFQ1RJVklUWUxFTkRBUlZFT1RJRllQVElPTlNDSFNFQVlTVEFUQ0hHRVVFUllPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFVFRQQ0VUU1BBRFRQLw=='\n\nlet wasmBuffer\n\nObject.defineProperty(module, 'exports', {\n  get: () => {\n    return wasmBuffer\n      ? wasmBuffer\n      : (wasmBuffer = Buffer.from(wasmBase64, 'base64'))\n  }\n})\n"
  },
  {
    "path": "lib/llhttp/llhttp_simd-wasm.js",
    "content": "'use strict'\n\nconst { Buffer } = require('node:buffer')\n\nconst wasmBase64 = 'AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAn9/AGABfwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAzU0BQYAAAMAAAAAAAADAQMAAwMDAAACAAAAAAICAgICAgICAgIBAQEBAQEBAQEBAwAAAwAAAAQFAXABExMFAwEAAgYIAX8BQcDZBAsHxQcoBm1lbW9yeQIAC19pbml0aWFsaXplAAgZX19pbmRpcmVjdF9mdW5jdGlvbl90YWJsZQEAC2xsaHR0cF9pbml0AAkYbGxodHRwX3Nob3VsZF9rZWVwX2FsaXZlADcMbGxodHRwX2FsbG9jAAsGbWFsbG9jADkLbGxodHRwX2ZyZWUADARmcmVlAAwPbGxodHRwX2dldF90eXBlAA0VbGxodHRwX2dldF9odHRwX21ham9yAA4VbGxodHRwX2dldF9odHRwX21pbm9yAA8RbGxodHRwX2dldF9tZXRob2QAEBZsbGh0dHBfZ2V0X3N0YXR1c19jb2RlABESbGxodHRwX2dldF91cGdyYWRlABIMbGxodHRwX3Jlc2V0ABMObGxodHRwX2V4ZWN1dGUAFBRsbGh0dHBfc2V0dGluZ3NfaW5pdAAVDWxsaHR0cF9maW5pc2gAFgxsbGh0dHBfcGF1c2UAFw1sbGh0dHBfcmVzdW1lABgbbGxodHRwX3Jlc3VtZV9hZnRlcl91cGdyYWRlABkQbGxodHRwX2dldF9lcnJubwAaF2xsaHR0cF9nZXRfZXJyb3JfcmVhc29uABsXbGxodHRwX3NldF9lcnJvcl9yZWFzb24AHBRsbGh0dHBfZ2V0X2Vycm9yX3BvcwAdEWxsaHR0cF9lcnJub19uYW1lAB4SbGxodHRwX21ldGhvZF9uYW1lAB8SbGxodHRwX3N0YXR1c19uYW1lACAabGxodHRwX3NldF9sZW5pZW50X2hlYWRlcnMAISFsbGh0dHBfc2V0X2xlbmllbnRfY2h1bmtlZF9sZW5ndGgAIh1sbGh0dHBfc2V0X2xlbmllbnRfa2VlcF9hbGl2ZQAjJGxsaHR0cF9zZXRfbGVuaWVudF90cmFuc2Zlcl9lbmNvZGluZwAkGmxsaHR0cF9zZXRfbGVuaWVudF92ZXJzaW9uACUjbGxodHRwX3NldF9sZW5pZW50X2RhdGFfYWZ0ZXJfY2xvc2UAJidsbGh0dHBfc2V0X2xlbmllbnRfb3B0aW9uYWxfbGZfYWZ0ZXJfY3IAJyxsbGh0dHBfc2V0X2xlbmllbnRfb3B0aW9uYWxfY3JsZl9hZnRlcl9jaHVuawAoKGxsaHR0cF9zZXRfbGVuaWVudF9vcHRpb25hbF9jcl9iZWZvcmVfbGYAKSpsbGh0dHBfc2V0X2xlbmllbnRfc3BhY2VzX2FmdGVyX2NodW5rX3NpemUAKhhsbGh0dHBfbWVzc2FnZV9uZWVkc19lb2YANgkYAQBBAQsSAQIDBAUKBgcyNDMuKy8tLDAxCuzaAjQWAEHA1QAoAgAEQAALQcDVAEEBNgIACxQAIAAQOCAAIAI2AjggACABOgAoCxQAIAAgAC8BNCAALQAwIAAQNxAACx4BAX9BwAAQOiIBEDggAUGACDYCOCABIAA6ACggAQuPDAEHfwJAIABFDQAgAEEIayIBIABBBGsoAgAiAEF4cSIEaiEFAkAgAEEBcQ0AIABBA3FFDQEgASABKAIAIgBrIgFB1NUAKAIASQ0BIAAgBGohBAJAAkBB2NUAKAIAIAFHBEAgAEH/AU0EQCAAQQN2IQMgASgCCCIAIAEoAgwiAkYEQEHE1QBBxNUAKAIAQX4gA3dxNgIADAULIAIgADYCCCAAIAI2AgwMBAsgASgCGCEGIAEgASgCDCIARwRAIAAgASgCCCICNgIIIAIgADYCDAwDCyABQRRqIgMoAgAiAkUEQCABKAIQIgJFDQIgAUEQaiEDCwNAIAMhByACIgBBFGoiAygCACICDQAgAEEQaiEDIAAoAhAiAg0ACyAHQQA2AgAMAgsgBSgCBCIAQQNxQQNHDQIgBSAAQX5xNgIEQczVACAENgIAIAUgBDYCACABIARBAXI2AgQMAwtBACEACyAGRQ0AAkAgASgCHCICQQJ0QfTXAGoiAygCACABRgRAIAMgADYCACAADQFByNUAQcjVACgCAEF+IAJ3cTYCAAwCCyAGQRBBFCAGKAIQIAFGG2ogADYCACAARQ0BCyAAIAY2AhggASgCECICBEAgACACNgIQIAIgADYCGAsgAUEUaigCACICRQ0AIABBFGogAjYCACACIAA2AhgLIAEgBU8NACAFKAIEIgBBAXFFDQACQAJAAkACQCAAQQJxRQRAQdzVACgCACAFRgRAQdzVACABNgIAQdDVAEHQ1QAoAgAgBGoiADYCACABIABBAXI2AgQgAUHY1QAoAgBHDQZBzNUAQQA2AgBB2NUAQQA2AgAMBgtB2NUAKAIAIAVGBEBB2NUAIAE2AgBBzNUAQczVACgCACAEaiIANgIAIAEgAEEBcjYCBCAAIAFqIAA2AgAMBgsgAEF4cSAEaiEEIABB/wFNBEAgAEEDdiEDIAUoAggiACAFKAIMIgJGBEBBxNUAQcTVACgCAEF+IAN3cTYCAAwFCyACIAA2AgggACACNgIMDAQLIAUoAhghBiAFIAUoAgwiAEcEQEHU1QAoAgAaIAAgBSgCCCICNgIIIAIgADYCDAwDCyAFQRRqIgMoAgAiAkUEQCAFKAIQIgJFDQIgBUEQaiEDCwNAIAMhByACIgBBFGoiAygCACICDQAgAEEQaiEDIAAoAhAiAg0ACyAHQQA2AgAMAgsgBSAAQX5xNgIEIAEgBGogBDYCACABIARBAXI2AgQMAwtBACEACyAGRQ0AAkAgBSgCHCICQQJ0QfTXAGoiAygCACAFRgRAIAMgADYCACAADQFByNUAQcjVACgCAEF+IAJ3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogADYCACAARQ0BCyAAIAY2AhggBSgCECICBEAgACACNgIQIAIgADYCGAsgBUEUaigCACICRQ0AIABBFGogAjYCACACIAA2AhgLIAEgBGogBDYCACABIARBAXI2AgQgAUHY1QAoAgBHDQBBzNUAIAQ2AgAMAQsgBEH/AU0EQCAEQXhxQezVAGohAAJ/QcTVACgCACICQQEgBEEDdnQiA3FFBEBBxNUAIAIgA3I2AgAgAAwBCyAAKAIICyICIAE2AgwgACABNgIIIAEgADYCDCABIAI2AggMAQtBHyECIARB////B00EQCAEQSYgBEEIdmciAGt2QQFxIABBAXRrQT5qIQILIAEgAjYCHCABQgA3AhAgAkECdEH01wBqIQACQEHI1QAoAgAiA0EBIAJ0IgdxRQRAIAAgATYCAEHI1QAgAyAHcjYCACABIAA2AhggASABNgIIIAEgATYCDAwBCyAEQRkgAkEBdmtBACACQR9HG3QhAiAAKAIAIQACQANAIAAiAygCBEF4cSAERg0BIAJBHXYhACACQQF0IQIgAyAAQQRxakEQaiIHKAIAIgANAAsgByABNgIAIAEgAzYCGCABIAE2AgwgASABNgIIDAELIAMoAggiACABNgIMIAMgATYCCCABQQA2AhggASADNgIMIAEgADYCCAtB5NUAQeTVACgCAEEBayIAQX8gABs2AgALCwcAIAAtACgLBwAgAC0AKgsHACAALQArCwcAIAAtACkLBwAgAC8BNAsHACAALQAwC0ABBH8gACgCGCEBIAAvAS4hAiAALQAoIQMgACgCOCEEIAAQOCAAIAQ2AjggACADOgAoIAAgAjsBLiAAIAE2AhgLhocCAwd/A34BeyABIAJqIQQCQCAAIgMoAgwiAA0AIAMoAgQEQCADIAE2AgQLIwBBEGsiCSQAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADKAIcIgJBAmsO/AEB+QECAwQFBgcICQoLDA0ODxAREvgBE/cBFBX2ARYX9QEYGRobHB0eHyD9AfsBIfQBIiMkJSYnKCkqK/MBLC0uLzAxMvIB8QEzNPAB7wE1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk/6AVBRUlPuAe0BVOwBVesBVldYWVrqAVtcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPAZABkQGSAZMBlAGVAZYBlwGYAZkBmgGbAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBqQGqAasBrAGtAa4BrwGwAbEBsgGzAbQBtQG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAekB6AHPAecB0AHmAdEB0gHTAdQB5QHVAdYB1wHYAdkB2gHbAdwB3QHeAd8B4AHhAeIB4wEA/AELQQAM4wELQQ4M4gELQQ0M4QELQQ8M4AELQRAM3wELQRMM3gELQRQM3QELQRUM3AELQRYM2wELQRcM2gELQRgM2QELQRkM2AELQRoM1wELQRsM1gELQRwM1QELQR0M1AELQR4M0wELQR8M0gELQSAM0QELQSEM0AELQQgMzwELQSIMzgELQSQMzQELQSMMzAELQQcMywELQSUMygELQSYMyQELQScMyAELQSgMxwELQRIMxgELQREMxQELQSkMxAELQSoMwwELQSsMwgELQSwMwQELQd4BDMABC0EuDL8BC0EvDL4BC0EwDL0BC0ExDLwBC0EyDLsBC0EzDLoBC0E0DLkBC0HfAQy4AQtBNQy3AQtBOQy2AQtBDAy1AQtBNgy0AQtBNwyzAQtBOAyyAQtBPgyxAQtBOgywAQtB4AEMrwELQQsMrgELQT8MrQELQTsMrAELQQoMqwELQTwMqgELQT0MqQELQeEBDKgBC0HBAAynAQtBwAAMpgELQcIADKUBC0EJDKQBC0EtDKMBC0HDAAyiAQtBxAAMoQELQcUADKABC0HGAAyfAQtBxwAMngELQcgADJ0BC0HJAAycAQtBygAMmwELQcsADJoBC0HMAAyZAQtBzQAMmAELQc4ADJcBC0HPAAyWAQtB0AAMlQELQdEADJQBC0HSAAyTAQtB0wAMkgELQdUADJEBC0HUAAyQAQtB1gAMjwELQdcADI4BC0HYAAyNAQtB2QAMjAELQdoADIsBC0HbAAyKAQtB3AAMiQELQd0ADIgBC0HeAAyHAQtB3wAMhgELQeAADIUBC0HhAAyEAQtB4gAMgwELQeMADIIBC0HkAAyBAQtB5QAMgAELQeIBDH8LQeYADH4LQecADH0LQQYMfAtB6AAMewtBBQx6C0HpAAx5C0EEDHgLQeoADHcLQesADHYLQewADHULQe0ADHQLQQMMcwtB7gAMcgtB7wAMcQtB8AAMcAtB8gAMbwtB8QAMbgtB8wAMbQtB9AAMbAtB9QAMawtB9gAMagtBAgxpC0H3AAxoC0H4AAxnC0H5AAxmC0H6AAxlC0H7AAxkC0H8AAxjC0H9AAxiC0H+AAxhC0H/AAxgC0GAAQxfC0GBAQxeC0GCAQxdC0GDAQxcC0GEAQxbC0GFAQxaC0GGAQxZC0GHAQxYC0GIAQxXC0GJAQxWC0GKAQxVC0GLAQxUC0GMAQxTC0GNAQxSC0GOAQxRC0GPAQxQC0GQAQxPC0GRAQxOC0GSAQxNC0GTAQxMC0GUAQxLC0GVAQxKC0GWAQxJC0GXAQxIC0GYAQxHC0GZAQxGC0GaAQxFC0GbAQxEC0GcAQxDC0GdAQxCC0GeAQxBC0GfAQxAC0GgAQw/C0GhAQw+C0GiAQw9C0GjAQw8C0GkAQw7C0GlAQw6C0GmAQw5C0GnAQw4C0GoAQw3C0GpAQw2C0GqAQw1C0GrAQw0C0GsAQwzC0GtAQwyC0GuAQwxC0GvAQwwC0GwAQwvC0GxAQwuC0GyAQwtC0GzAQwsC0G0AQwrC0G1AQwqC0G2AQwpC0G3AQwoC0G4AQwnC0G5AQwmC0G6AQwlC0G7AQwkC0G8AQwjC0G9AQwiC0G+AQwhC0G/AQwgC0HAAQwfC0HBAQweC0HCAQwdC0EBDBwLQcMBDBsLQcQBDBoLQcUBDBkLQcYBDBgLQccBDBcLQcgBDBYLQckBDBULQcoBDBQLQcsBDBMLQcwBDBILQc0BDBELQc4BDBALQc8BDA8LQdABDA4LQdEBDA0LQdIBDAwLQdMBDAsLQdQBDAoLQdUBDAkLQdYBDAgLQeMBDAcLQdcBDAYLQdgBDAULQdkBDAQLQdoBDAMLQdsBDAILQd0BDAELQdwBCyECA0ACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAMCfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAn8CQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAwJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCACDuMBAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISMkJScoKZ4DmwOaA5EDigODA4AD/QL7AvgC8gLxAu8C7QLoAucC5gLlAuQC3ALbAtoC2QLYAtcC1gLVAs8CzgLMAssCygLJAsgCxwLGAsQCwwK+ArwCugK5ArgCtwK2ArUCtAKzArICsQKwAq4CrQKpAqgCpwKmAqUCpAKjAqICoQKgAp8CmAKQAowCiwKKAoEC/gH9AfwB+wH6AfkB+AH3AfUB8wHwAesB6QHoAecB5gHlAeQB4wHiAeEB4AHfAd4B3QHcAdoB2QHYAdcB1gHVAdQB0wHSAdEB0AHPAc4BzQHMAcsBygHJAcgBxwHGAcUBxAHDAcIBwQHAAb8BvgG9AbwBuwG6AbkBuAG3AbYBtQG0AbMBsgGxAbABrwGuAa0BrAGrAaoBqQGoAacBpgGlAaQBowGiAZ8BngGZAZgBlwGWAZUBlAGTAZIBkQGQAY8BjQGMAYcBhgGFAYQBgwGCAX18e3p5dnV0UFFSU1RVCyABIARHDXJB/QEhAgy+AwsgASAERw2YAUHbASECDL0DCyABIARHDfEBQY4BIQIMvAMLIAEgBEcN/AFBhAEhAgy7AwsgASAERw2KAkH/ACECDLoDCyABIARHDZECQf0AIQIMuQMLIAEgBEcNlAJB+wAhAgy4AwsgASAERw0eQR4hAgy3AwsgASAERw0ZQRghAgy2AwsgASAERw3KAkHNACECDLUDCyABIARHDdUCQcYAIQIMtAMLIAEgBEcN1gJBwwAhAgyzAwsgASAERw3cAkE4IQIMsgMLIAMtADBBAUYNrQMMiQMLQQAhAAJAAkACQCADLQAqRQ0AIAMtACtFDQAgAy8BMiICQQJxRQ0BDAILIAMvATIiAkEBcUUNAQtBASEAIAMtAChBAUYNACADLwE0IgZB5ABrQeQASQ0AIAZBzAFGDQAgBkGwAkYNACACQcAAcQ0AQQAhACACQYgEcUGABEYNACACQShxQQBHIQALIANBADsBMiADQQA6ADECQCAARQRAIANBADoAMSADLQAuQQRxDQEMsQMLIANCADcDIAsgA0EAOgAxIANBAToANgxIC0EAIQACQCADKAI4IgJFDQAgAigCMCICRQ0AIAMgAhEAACEACyAARQ1IIABBFUcNYiADQQQ2AhwgAyABNgIUIANB0hs2AhAgA0EVNgIMQQAhAgyvAwsgASAERgRAQQYhAgyvAwsgAS0AAEEKRw0ZIAFBAWohAQwaCyADQgA3AyBBEiECDJQDCyABIARHDYoDQSMhAgysAwsgASAERgRAQQchAgysAwsCQAJAIAEtAABBCmsOBAEYGAAYCyABQQFqIQFBECECDJMDCyABQQFqIQEgA0Evai0AAEEBcQ0XQQAhAiADQQA2AhwgAyABNgIUIANBmSA2AhAgA0EZNgIMDKsDCyADIAMpAyAiDCAEIAFrrSIKfSILQgAgCyAMWBs3AyAgCiAMWg0YQQghAgyqAwsgASAERwRAIANBCTYCCCADIAE2AgRBFCECDJEDC0EJIQIMqQMLIAMpAyBQDa4CDEMLIAEgBEYEQEELIQIMqAMLIAEtAABBCkcNFiABQQFqIQEMFwsgA0Evai0AAEEBcUUNGQwmC0EAIQACQCADKAI4IgJFDQAgAigCUCICRQ0AIAMgAhEAACEACyAADRkMQgtBACEAAkAgAygCOCICRQ0AIAIoAlAiAkUNACADIAIRAAAhAAsgAA0aDCQLQQAhAAJAIAMoAjgiAkUNACACKAJQIgJFDQAgAyACEQAAIQALIAANGwwyCyADQS9qLQAAQQFxRQ0cDCILQQAhAAJAIAMoAjgiAkUNACACKAJUIgJFDQAgAyACEQAAIQALIAANHAxCC0EAIQACQCADKAI4IgJFDQAgAigCVCICRQ0AIAMgAhEAACEACyAADR0MIAsgASAERgRAQRMhAgygAwsCQCABLQAAIgBBCmsOBB8jIwAiCyABQQFqIQEMHwtBACEAAkAgAygCOCICRQ0AIAIoAlQiAkUNACADIAIRAAAhAAsgAA0iDEILIAEgBEYEQEEWIQIMngMLIAEtAABBwMEAai0AAEEBRw0jDIMDCwJAA0AgAS0AAEGwO2otAAAiAEEBRwRAAkAgAEECaw4CAwAnCyABQQFqIQFBISECDIYDCyAEIAFBAWoiAUcNAAtBGCECDJ0DCyADKAIEIQBBACECIANBADYCBCADIAAgAUEBaiIBEDQiAA0hDEELQQAhAAJAIAMoAjgiAkUNACACKAJUIgJFDQAgAyACEQAAIQALIAANIwwqCyABIARGBEBBHCECDJsDCyADQQo2AgggAyABNgIEQQAhAAJAIAMoAjgiAkUNACACKAJQIgJFDQAgAyACEQAAIQALIAANJUEkIQIMgQMLIAEgBEcEQANAIAEtAABBsD1qLQAAIgBBA0cEQCAAQQFrDgUYGiaCAyUmCyAEIAFBAWoiAUcNAAtBGyECDJoDC0EbIQIMmQMLA0AgAS0AAEGwP2otAAAiAEEDRwRAIABBAWsOBQ8RJxMmJwsgBCABQQFqIgFHDQALQR4hAgyYAwsgASAERwRAIANBCzYCCCADIAE2AgRBByECDP8CC0EfIQIMlwMLIAEgBEYEQEEgIQIMlwMLAkAgAS0AAEENaw4ULj8/Pz8/Pz8/Pz8/Pz8/Pz8/PwA/C0EAIQIgA0EANgIcIANBvws2AhAgA0ECNgIMIAMgAUEBajYCFAyWAwsgA0EvaiECA0AgASAERgRAQSEhAgyXAwsCQAJAAkAgAS0AACIAQQlrDhgCACkpASkpKSkpKSkpKSkpKSkpKSkpKQInCyABQQFqIQEgA0Evai0AAEEBcUUNCgwYCyABQQFqIQEMFwsgAUEBaiEBIAItAABBAnENAAtBACECIANBADYCHCADIAE2AhQgA0GfFTYCECADQQw2AgwMlQMLIAMtAC5BgAFxRQ0BC0EAIQACQCADKAI4IgJFDQAgAigCXCICRQ0AIAMgAhEAACEACyAARQ3mAiAAQRVGBEAgA0EkNgIcIAMgATYCFCADQZsbNgIQIANBFTYCDEEAIQIMlAMLQQAhAiADQQA2AhwgAyABNgIUIANBkA42AhAgA0EUNgIMDJMDC0EAIQIgA0EANgIcIAMgATYCFCADQb4gNgIQIANBAjYCDAySAwsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEgDKdqIgEQMiIARQ0rIANBBzYCHCADIAE2AhQgAyAANgIMDJEDCyADLQAuQcAAcUUNAQtBACEAAkAgAygCOCICRQ0AIAIoAlgiAkUNACADIAIRAAAhAAsgAEUNKyAAQRVGBEAgA0EKNgIcIAMgATYCFCADQesZNgIQIANBFTYCDEEAIQIMkAMLQQAhAiADQQA2AhwgAyABNgIUIANBkww2AhAgA0ETNgIMDI8DC0EAIQIgA0EANgIcIAMgATYCFCADQYIVNgIQIANBAjYCDAyOAwtBACECIANBADYCHCADIAE2AhQgA0HdFDYCECADQRk2AgwMjQMLQQAhAiADQQA2AhwgAyABNgIUIANB5h02AhAgA0EZNgIMDIwDCyAAQRVGDT1BACECIANBADYCHCADIAE2AhQgA0HQDzYCECADQSI2AgwMiwMLIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDMiAEUNKCADQQ02AhwgAyABNgIUIAMgADYCDAyKAwsgAEEVRg06QQAhAiADQQA2AhwgAyABNgIUIANB0A82AhAgA0EiNgIMDIkDCyADKAIEIQBBACECIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDCgLIANBDjYCHCADIAA2AgwgAyABQQFqNgIUDIgDCyAAQRVGDTdBACECIANBADYCHCADIAE2AhQgA0HQDzYCECADQSI2AgwMhwMLIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDMiAEUEQCABQQFqIQEMJwsgA0EPNgIcIAMgADYCDCADIAFBAWo2AhQMhgMLQQAhAiADQQA2AhwgAyABNgIUIANB4hc2AhAgA0EZNgIMDIUDCyAAQRVGDTNBACECIANBADYCHCADIAE2AhQgA0HWDDYCECADQSM2AgwMhAMLIAMoAgQhAEEAIQIgA0EANgIEIAMgACABEDQiAEUNJSADQRE2AhwgAyABNgIUIAMgADYCDAyDAwsgAEEVRg0wQQAhAiADQQA2AhwgAyABNgIUIANB1gw2AhAgA0EjNgIMDIIDCyADKAIEIQBBACECIANBADYCBCADIAAgARA0IgBFBEAgAUEBaiEBDCULIANBEjYCHCADIAA2AgwgAyABQQFqNgIUDIEDCyADQS9qLQAAQQFxRQ0BC0EXIQIM5gILQQAhAiADQQA2AhwgAyABNgIUIANB4hc2AhAgA0EZNgIMDP4CCyAAQTtHDQAgAUEBaiEBDAwLQQAhAiADQQA2AhwgAyABNgIUIANBkhg2AhAgA0ECNgIMDPwCCyAAQRVGDShBACECIANBADYCHCADIAE2AhQgA0HWDDYCECADQSM2AgwM+wILIANBFDYCHCADIAE2AhQgAyAANgIMDPoCCyADKAIEIQBBACECIANBADYCBCADIAAgARA0IgBFBEAgAUEBaiEBDPUCCyADQRU2AhwgAyAANgIMIAMgAUEBajYCFAz5AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQzzAgsgA0EXNgIcIAMgADYCDCADIAFBAWo2AhQM+AILIABBFUYNI0EAIQIgA0EANgIcIAMgATYCFCADQdYMNgIQIANBIzYCDAz3AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQwdCyADQRk2AhwgAyAANgIMIAMgAUEBajYCFAz2AgsgAygCBCEAQQAhAiADQQA2AgQgAyAAIAEQNCIARQRAIAFBAWohAQzvAgsgA0EaNgIcIAMgADYCDCADIAFBAWo2AhQM9QILIABBFUYNH0EAIQIgA0EANgIcIAMgATYCFCADQdAPNgIQIANBIjYCDAz0AgsgAygCBCEAIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDBsLIANBHDYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgzzAgsgAygCBCEAIANBADYCBCADIAAgARAzIgBFBEAgAUEBaiEBDOsCCyADQR02AhwgAyAANgIMIAMgAUEBajYCFEEAIQIM8gILIABBO0cNASABQQFqIQELQSYhAgzXAgtBACECIANBADYCHCADIAE2AhQgA0GfFTYCECADQQw2AgwM7wILIAEgBEcEQANAIAEtAABBIEcNhAIgBCABQQFqIgFHDQALQSwhAgzvAgtBLCECDO4CCyABIARGBEBBNCECDO4CCwJAAkADQAJAIAEtAABBCmsOBAIAAAMACyAEIAFBAWoiAUcNAAtBNCECDO8CCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUNnwIgA0EyNgIcIAMgATYCFCADIAA2AgxBACECDO4CCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUEQCABQQFqIQEMnwILIANBMjYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgztAgsgASAERwRAAkADQCABLQAAQTBrIgBB/wFxQQpPBEBBOiECDNcCCyADKQMgIgtCmbPmzJmz5swZVg0BIAMgC0IKfiIKNwMgIAogAK1C/wGDIgtCf4VWDQEgAyAKIAt8NwMgIAQgAUEBaiIBRw0AC0HAACECDO4CCyADKAIEIQAgA0EANgIEIAMgACABQQFqIgEQMSIADRcM4gILQcAAIQIM7AILIAEgBEYEQEHJACECDOwCCwJAA0ACQCABLQAAQQlrDhgAAqICogKpAqICogKiAqICogKiAqICogKiAqICogKiAqICogKiAqICogKiAgCiAgsgBCABQQFqIgFHDQALQckAIQIM7AILIAFBAWohASADQS9qLQAAQQFxDaUCIANBADYCHCADIAE2AhQgA0GXEDYCECADQQo2AgxBACECDOsCCyABIARHBEADQCABLQAAQSBHDRUgBCABQQFqIgFHDQALQfgAIQIM6wILQfgAIQIM6gILIANBAjoAKAw4C0EAIQIgA0EANgIcIANBvws2AhAgA0ECNgIMIAMgAUEBajYCFAzoAgtBACECDM4CC0ENIQIMzQILQRMhAgzMAgtBFSECDMsCC0EWIQIMygILQRghAgzJAgtBGSECDMgCC0EaIQIMxwILQRshAgzGAgtBHCECDMUCC0EdIQIMxAILQR4hAgzDAgtBHyECDMICC0EgIQIMwQILQSIhAgzAAgtBIyECDL8CC0ElIQIMvgILQeUAIQIMvQILIANBPTYCHCADIAE2AhQgAyAANgIMQQAhAgzVAgsgA0EbNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIM1AILIANBIDYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNMCCyADQRM2AhwgAyABNgIUIANBmBo2AhAgA0EVNgIMQQAhAgzSAgsgA0ELNgIcIAMgATYCFCADQZgaNgIQIANBFTYCDEEAIQIM0QILIANBEDYCHCADIAE2AhQgA0GYGjYCECADQRU2AgxBACECDNACCyADQSA2AhwgAyABNgIUIANBpBw2AhAgA0EVNgIMQQAhAgzPAgsgA0ELNgIcIAMgATYCFCADQaQcNgIQIANBFTYCDEEAIQIMzgILIANBDDYCHCADIAE2AhQgA0GkHDYCECADQRU2AgxBACECDM0CC0EAIQIgA0EANgIcIAMgATYCFCADQd0ONgIQIANBEjYCDAzMAgsCQANAAkAgAS0AAEEKaw4EAAICAAILIAQgAUEBaiIBRw0AC0H9ASECDMwCCwJAAkAgAy0ANkEBRw0AQQAhAAJAIAMoAjgiAkUNACACKAJgIgJFDQAgAyACEQAAIQALIABFDQAgAEEVRw0BIANB/AE2AhwgAyABNgIUIANB3Bk2AhAgA0EVNgIMQQAhAgzNAgtB3AEhAgyzAgsgA0EANgIcIAMgATYCFCADQfkLNgIQIANBHzYCDEEAIQIMywILAkACQCADLQAoQQFrDgIEAQALQdsBIQIMsgILQdQBIQIMsQILIANBAjoAMUEAIQACQCADKAI4IgJFDQAgAigCACICRQ0AIAMgAhEAACEACyAARQRAQd0BIQIMsQILIABBFUcEQCADQQA2AhwgAyABNgIUIANBtAw2AhAgA0EQNgIMQQAhAgzKAgsgA0H7ATYCHCADIAE2AhQgA0GBGjYCECADQRU2AgxBACECDMkCCyABIARGBEBB+gEhAgzJAgsgAS0AAEHIAEYNASADQQE6ACgLQcABIQIMrgILQdoBIQIMrQILIAEgBEcEQCADQQw2AgggAyABNgIEQdkBIQIMrQILQfkBIQIMxQILIAEgBEYEQEH4ASECDMUCCyABLQAAQcgARw0EIAFBAWohAUHYASECDKsCCyABIARGBEBB9wEhAgzEAgsCQAJAIAEtAABBxQBrDhAABQUFBQUFBQUFBQUFBQUBBQsgAUEBaiEBQdYBIQIMqwILIAFBAWohAUHXASECDKoCC0H2ASECIAEgBEYNwgIgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABButUAai0AAEcNAyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMwwILIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARAuIgBFBEBB4wEhAgyqAgsgA0H1ATYCHCADIAE2AhQgAyAANgIMQQAhAgzCAgtB9AEhAiABIARGDcECIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQbjVAGotAABHDQIgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADMICCyADQYEEOwEoIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARAuIgANAwwCCyADQQA2AgALQQAhAiADQQA2AhwgAyABNgIUIANB5R82AhAgA0EINgIMDL8CC0HVASECDKUCCyADQfMBNgIcIAMgATYCFCADIAA2AgxBACECDL0CC0EAIQACQCADKAI4IgJFDQAgAigCQCICRQ0AIAMgAhEAACEACyAARQ1uIABBFUcEQCADQQA2AhwgAyABNgIUIANBgg82AhAgA0EgNgIMQQAhAgy9AgsgA0GPATYCHCADIAE2AhQgA0HsGzYCECADQRU2AgxBACECDLwCCyABIARHBEAgA0ENNgIIIAMgATYCBEHTASECDKMCC0HyASECDLsCCyABIARGBEBB8QEhAgy7AgsCQAJAAkAgAS0AAEHIAGsOCwABCAgICAgICAgCCAsgAUEBaiEBQdABIQIMowILIAFBAWohAUHRASECDKICCyABQQFqIQFB0gEhAgyhAgtB8AEhAiABIARGDbkCIAMoAgAiACAEIAFraiEGIAEgAGtBAmohBQNAIAEtAAAgAEG11QBqLQAARw0EIABBAkYNAyAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy5AgtB7wEhAiABIARGDbgCIAMoAgAiACAEIAFraiEGIAEgAGtBAWohBQNAIAEtAAAgAEGz1QBqLQAARw0DIABBAUYNAiAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy4AgtB7gEhAiABIARGDbcCIAMoAgAiACAEIAFraiEGIAEgAGtBAmohBQNAIAEtAAAgAEGw1QBqLQAARw0CIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBjYCAAy3AgsgAygCBCEAIANCADcDACADIAAgBUEBaiIBECsiAEUNAiADQewBNgIcIAMgATYCFCADIAA2AgxBACECDLYCCyADQQA2AgALIAMoAgQhACADQQA2AgQgAyAAIAEQKyIARQ2cAiADQe0BNgIcIAMgATYCFCADIAA2AgxBACECDLQCC0HPASECDJoCC0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMtAILQc4BIQIMmgILIANB6wE2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyyAgsgASAERgRAQesBIQIMsgILIAEtAABBL0YEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQbI4NgIQIANBCDYCDEEAIQIMsQILQc0BIQIMlwILIAEgBEcEQCADQQ42AgggAyABNgIEQcwBIQIMlwILQeoBIQIMrwILIAEgBEYEQEHpASECDK8CCyABLQAAQTBrIgBB/wFxQQpJBEAgAyAAOgAqIAFBAWohAUHLASECDJYCCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNlwIgA0HoATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgsgASAERgRAQecBIQIMrgILAkAgAS0AAEEuRgRAIAFBAWohAQwBCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNmAIgA0HmATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgtBygEhAgyUAgsgASAERgRAQeUBIQIMrQILQQAhAEEBIQVBASEHQQAhAgJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAIAEtAABBMGsOCgoJAAECAwQFBggLC0ECDAYLQQMMBQtBBAwEC0EFDAMLQQYMAgtBBwwBC0EICyECQQAhBUEAIQcMAgtBCSECQQEhAEEAIQVBACEHDAELQQAhBUEBIQILIAMgAjoAKyABQQFqIQECQAJAIAMtAC5BEHENAAJAAkACQCADLQAqDgMBAAIECyAHRQ0DDAILIAANAQwCCyAFRQ0BCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNAiADQeIBNgIcIAMgATYCFCADIAA2AgxBACECDK8CCyADKAIEIQAgA0EANgIEIAMgACABEC8iAEUNmgIgA0HjATYCHCADIAE2AhQgAyAANgIMQQAhAgyuAgsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDZgCIANB5AE2AhwgAyABNgIUIAMgADYCDAytAgtByQEhAgyTAgtBACEAAkAgAygCOCICRQ0AIAIoAkQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0GkDTYCECADQSE2AgxBACECDK0CC0HIASECDJMCCyADQeEBNgIcIAMgATYCFCADQdAaNgIQIANBFTYCDEEAIQIMqwILIAEgBEYEQEHhASECDKsCCwJAIAEtAABBIEYEQCADQQA7ATQgAUEBaiEBDAELIANBADYCHCADIAE2AhQgA0GZETYCECADQQk2AgxBACECDKsCC0HHASECDJECCyABIARGBEBB4AEhAgyqAgsCQCABLQAAQTBrQf8BcSICQQpJBEAgAUEBaiEBAkAgAy8BNCIAQZkzSw0AIAMgAEEKbCIAOwE0IABB/v8DcSACQf//A3NLDQAgAyAAIAJqOwE0DAILQQAhAiADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMDKsCCyADQQA2AhwgAyABNgIUIANBlR42AhAgA0ENNgIMQQAhAgyqAgtBxgEhAgyQAgsgASAERgRAQd8BIQIMqQILAkAgAS0AAEEwa0H/AXEiAkEKSQRAIAFBAWohAQJAIAMvATQiAEGZM0sNACADIABBCmwiADsBNCAAQf7/A3EgAkH//wNzSw0AIAMgACACajsBNAwCC0EAIQIgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDAyqAgsgA0EANgIcIAMgATYCFCADQZUeNgIQIANBDTYCDEEAIQIMqQILQcUBIQIMjwILIAEgBEYEQEHeASECDKgCCwJAIAEtAABBMGtB/wFxIgJBCkkEQCABQQFqIQECQCADLwE0IgBBmTNLDQAgAyAAQQpsIgA7ATQgAEH+/wNxIAJB//8Dc0sNACADIAAgAmo7ATQMAgtBACECIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgwMqQILIANBADYCHCADIAE2AhQgA0GVHjYCECADQQ02AgxBACECDKgCC0HEASECDI4CCyABIARGBEBB3QEhAgynAgsCQAJAAkACQCABLQAAQQprDhcCAwMAAwMDAwMDAwMDAwMDAwMDAwMDAQMLIAFBAWoMBQsgAUEBaiEBQcMBIQIMjwILIAFBAWohASADQS9qLQAAQQFxDQggA0EANgIcIAMgATYCFCADQY0LNgIQIANBDTYCDEEAIQIMpwILIANBADYCHCADIAE2AhQgA0GNCzYCECADQQ02AgxBACECDKYCCyABIARHBEAgA0EPNgIIIAMgATYCBEEBIQIMjQILQdwBIQIMpQILAkACQANAAkAgAS0AAEEKaw4EAgAAAwALIAQgAUEBaiIBRw0AC0HbASECDKYCCyADKAIEIQAgA0EANgIEIAMgACABEC0iAEUEQCABQQFqIQEMBAsgA0HaATYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgylAgsgAygCBCEAIANBADYCBCADIAAgARAtIgANASABQQFqCyEBQcEBIQIMigILIANB2QE2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMogILQcIBIQIMiAILIANBL2otAABBAXENASADQQA2AhwgAyABNgIUIANB5Bw2AhAgA0EZNgIMQQAhAgygAgsgASAERgRAQdkBIQIMoAILAkACQAJAIAEtAABBCmsOBAECAgACCyABQQFqIQEMAgsgAUEBaiEBDAELIAMtAC5BwABxRQ0BC0EAIQACQCADKAI4IgJFDQAgAigCPCICRQ0AIAMgAhEAACEACyAARQ2gASAAQRVGBEAgA0HZADYCHCADIAE2AhQgA0G3GjYCECADQRU2AgxBACECDJ8CCyADQQA2AhwgAyABNgIUIANBgA02AhAgA0EbNgIMQQAhAgyeAgsgA0EANgIcIAMgATYCFCADQdwoNgIQIANBAjYCDEEAIQIMnQILIAEgBEcEQCADQQw2AgggAyABNgIEQb8BIQIMhAILQdgBIQIMnAILIAEgBEYEQEHXASECDJwCCwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAS0AAEHBAGsOFQABAgNaBAUGWlpaBwgJCgsMDQ4PEFoLIAFBAWohAUH7ACECDJICCyABQQFqIQFB/AAhAgyRAgsgAUEBaiEBQYEBIQIMkAILIAFBAWohAUGFASECDI8CCyABQQFqIQFBhgEhAgyOAgsgAUEBaiEBQYkBIQIMjQILIAFBAWohAUGKASECDIwCCyABQQFqIQFBjQEhAgyLAgsgAUEBaiEBQZYBIQIMigILIAFBAWohAUGXASECDIkCCyABQQFqIQFBmAEhAgyIAgsgAUEBaiEBQaUBIQIMhwILIAFBAWohAUGmASECDIYCCyABQQFqIQFBrAEhAgyFAgsgAUEBaiEBQbQBIQIMhAILIAFBAWohAUG3ASECDIMCCyABQQFqIQFBvgEhAgyCAgsgASAERgRAQdYBIQIMmwILIAEtAABBzgBHDUggAUEBaiEBQb0BIQIMgQILIAEgBEYEQEHVASECDJoCCwJAAkACQCABLQAAQcIAaw4SAEpKSkpKSkpKSgFKSkpKSkoCSgsgAUEBaiEBQbgBIQIMggILIAFBAWohAUG7ASECDIECCyABQQFqIQFBvAEhAgyAAgtB1AEhAiABIARGDZgCIAMoAgAiACAEIAFraiEFIAEgAGtBB2ohBgJAA0AgAS0AACAAQajVAGotAABHDUUgAEEHRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJkCCyADQQA2AgAgBkEBaiEBQRsMRQsgASAERgRAQdMBIQIMmAILAkACQCABLQAAQckAaw4HAEdHR0dHAUcLIAFBAWohAUG5ASECDP8BCyABQQFqIQFBugEhAgz+AQtB0gEhAiABIARGDZYCIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQabVAGotAABHDUMgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJcCCyADQQA2AgAgBkEBaiEBQQ8MQwtB0QEhAiABIARGDZUCIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQaTVAGotAABHDUIgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJYCCyADQQA2AgAgBkEBaiEBQSAMQgtB0AEhAiABIARGDZQCIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQaHVAGotAABHDUEgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADJUCCyADQQA2AgAgBkEBaiEBQRIMQQsgASAERgRAQc8BIQIMlAILAkACQCABLQAAQcUAaw4OAENDQ0NDQ0NDQ0NDQwFDCyABQQFqIQFBtQEhAgz7AQsgAUEBaiEBQbYBIQIM+gELQc4BIQIgASAERg2SAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGe1QBqLQAARw0/IABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyTAgsgA0EANgIAIAZBAWohAUEHDD8LQc0BIQIgASAERg2RAiADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEGY1QBqLQAARw0+IABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAySAgsgA0EANgIAIAZBAWohAUEoDD4LIAEgBEYEQEHMASECDJECCwJAAkACQCABLQAAQcUAaw4RAEFBQUFBQUFBQQFBQUFBQQJBCyABQQFqIQFBsQEhAgz5AQsgAUEBaiEBQbIBIQIM+AELIAFBAWohAUGzASECDPcBC0HLASECIAEgBEYNjwIgAygCACIAIAQgAWtqIQUgASAAa0EGaiEGAkADQCABLQAAIABBkdUAai0AAEcNPCAAQQZGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMkAILIANBADYCACAGQQFqIQFBGgw8C0HKASECIAEgBEYNjgIgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABBjdUAai0AAEcNOyAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMjwILIANBADYCACAGQQFqIQFBIQw7CyABIARGBEBByQEhAgyOAgsCQAJAIAEtAABBwQBrDhQAPT09PT09PT09PT09PT09PT09AT0LIAFBAWohAUGtASECDPUBCyABQQFqIQFBsAEhAgz0AQsgASAERgRAQcgBIQIMjQILAkACQCABLQAAQdUAaw4LADw8PDw8PDw8PAE8CyABQQFqIQFBrgEhAgz0AQsgAUEBaiEBQa8BIQIM8wELQccBIQIgASAERg2LAiADKAIAIgAgBCABa2ohBSABIABrQQhqIQYCQANAIAEtAAAgAEGE1QBqLQAARw04IABBCEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyMAgsgA0EANgIAIAZBAWohAUEqDDgLIAEgBEYEQEHGASECDIsCCyABLQAAQdAARw04IAFBAWohAUElDDcLQcUBIQIgASAERg2JAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGB1QBqLQAARw02IABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyKAgsgA0EANgIAIAZBAWohAUEODDYLIAEgBEYEQEHEASECDIkCCyABLQAAQcUARw02IAFBAWohAUGrASECDO8BCyABIARGBEBBwwEhAgyIAgsCQAJAAkACQCABLQAAQcIAaw4PAAECOTk5OTk5OTk5OTkDOQsgAUEBaiEBQacBIQIM8QELIAFBAWohAUGoASECDPABCyABQQFqIQFBqQEhAgzvAQsgAUEBaiEBQaoBIQIM7gELQcIBIQIgASAERg2GAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEH+1ABqLQAARw0zIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyHAgsgA0EANgIAIAZBAWohAUEUDDMLQcEBIQIgASAERg2FAiADKAIAIgAgBCABa2ohBSABIABrQQRqIQYCQANAIAEtAAAgAEH51ABqLQAARw0yIABBBEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyGAgsgA0EANgIAIAZBAWohAUErDDILQcABIQIgASAERg2EAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEH21ABqLQAARw0xIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyFAgsgA0EANgIAIAZBAWohAUEsDDELQb8BIQIgASAERg2DAiADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGh1QBqLQAARw0wIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyEAgsgA0EANgIAIAZBAWohAUERDDALQb4BIQIgASAERg2CAiADKAIAIgAgBCABa2ohBSABIABrQQNqIQYCQANAIAEtAAAgAEHy1ABqLQAARw0vIABBA0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyDAgsgA0EANgIAIAZBAWohAUEuDC8LIAEgBEYEQEG9ASECDIICCwJAAkACQAJAAkAgAS0AAEHBAGsOFQA0NDQ0NDQ0NDQ0ATQ0AjQ0AzQ0BDQLIAFBAWohAUGbASECDOwBCyABQQFqIQFBnAEhAgzrAQsgAUEBaiEBQZ0BIQIM6gELIAFBAWohAUGiASECDOkBCyABQQFqIQFBpAEhAgzoAQsgASAERgRAQbwBIQIMgQILAkACQCABLQAAQdIAaw4DADABMAsgAUEBaiEBQaMBIQIM6AELIAFBAWohAUEEDC0LQbsBIQIgASAERg3/ASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHw1ABqLQAARw0sIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyAAgsgA0EANgIAIAZBAWohAUEdDCwLIAEgBEYEQEG6ASECDP8BCwJAAkAgAS0AAEHJAGsOBwEuLi4uLgAuCyABQQFqIQFBoQEhAgzmAQsgAUEBaiEBQSIMKwsgASAERgRAQbkBIQIM/gELIAEtAABB0ABHDSsgAUEBaiEBQaABIQIM5AELIAEgBEYEQEG4ASECDP0BCwJAAkAgAS0AAEHGAGsOCwAsLCwsLCwsLCwBLAsgAUEBaiEBQZ4BIQIM5AELIAFBAWohAUGfASECDOMBC0G3ASECIAEgBEYN+wEgAygCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABB7NQAai0AAEcNKCAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM/AELIANBADYCACAGQQFqIQFBDQwoC0G2ASECIAEgBEYN+gEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBodUAai0AAEcNJyAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM+wELIANBADYCACAGQQFqIQFBDAwnC0G1ASECIAEgBEYN+QEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABB6tQAai0AAEcNJiAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM+gELIANBADYCACAGQQFqIQFBAwwmC0G0ASECIAEgBEYN+AEgAygCACIAIAQgAWtqIQUgASAAa0EBaiEGAkADQCABLQAAIABB6NQAai0AAEcNJSAAQQFGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM+QELIANBADYCACAGQQFqIQFBJgwlCyABIARGBEBBswEhAgz4AQsCQAJAIAEtAABB1ABrDgIAAScLIAFBAWohAUGZASECDN8BCyABQQFqIQFBmgEhAgzeAQtBsgEhAiABIARGDfYBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQebUAGotAABHDSMgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPcBCyADQQA2AgAgBkEBaiEBQScMIwtBsQEhAiABIARGDfUBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQeTUAGotAABHDSIgAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPYBCyADQQA2AgAgBkEBaiEBQRwMIgtBsAEhAiABIARGDfQBIAMoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQd7UAGotAABHDSEgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPUBCyADQQA2AgAgBkEBaiEBQQYMIQtBrwEhAiABIARGDfMBIAMoAgAiACAEIAFraiEFIAEgAGtBBGohBgJAA0AgAS0AACAAQdnUAGotAABHDSAgAEEERg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPQBCyADQQA2AgAgBkEBaiEBQRkMIAsgASAERgRAQa4BIQIM8wELAkACQAJAAkAgAS0AAEEtaw4jACQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkASQkJCQkAiQkJAMkCyABQQFqIQFBjgEhAgzcAQsgAUEBaiEBQY8BIQIM2wELIAFBAWohAUGUASECDNoBCyABQQFqIQFBlQEhAgzZAQtBrQEhAiABIARGDfEBIAMoAgAiACAEIAFraiEFIAEgAGtBAWohBgJAA0AgAS0AACAAQdfUAGotAABHDR4gAEEBRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADPIBCyADQQA2AgAgBkEBaiEBQQsMHgsgASAERgRAQawBIQIM8QELAkACQCABLQAAQcEAaw4DACABIAsgAUEBaiEBQZABIQIM2AELIAFBAWohAUGTASECDNcBCyABIARGBEBBqwEhAgzwAQsCQAJAIAEtAABBwQBrDg8AHx8fHx8fHx8fHx8fHwEfCyABQQFqIQFBkQEhAgzXAQsgAUEBaiEBQZIBIQIM1gELIAEgBEYEQEGqASECDO8BCyABLQAAQcwARw0cIAFBAWohAUEKDBsLQakBIQIgASAERg3tASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEHR1ABqLQAARw0aIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzuAQsgA0EANgIAIAZBAWohAUEeDBoLQagBIQIgASAERg3sASADKAIAIgAgBCABa2ohBSABIABrQQZqIQYCQANAIAEtAAAgAEHK1ABqLQAARw0ZIABBBkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAztAQsgA0EANgIAIAZBAWohAUEVDBkLQacBIQIgASAERg3rASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEHH1ABqLQAARw0YIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzsAQsgA0EANgIAIAZBAWohAUEXDBgLQaYBIQIgASAERg3qASADKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEHB1ABqLQAARw0XIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzrAQsgA0EANgIAIAZBAWohAUEYDBcLIAEgBEYEQEGlASECDOoBCwJAAkAgAS0AAEHJAGsOBwAZGRkZGQEZCyABQQFqIQFBiwEhAgzRAQsgAUEBaiEBQYwBIQIM0AELQaQBIQIgASAERg3oASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGm1QBqLQAARw0VIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzpAQsgA0EANgIAIAZBAWohAUEJDBULQaMBIQIgASAERg3nASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGk1QBqLQAARw0UIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzoAQsgA0EANgIAIAZBAWohAUEfDBQLQaIBIQIgASAERg3mASADKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEG+1ABqLQAARw0TIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAznAQsgA0EANgIAIAZBAWohAUECDBMLQaEBIQIgASAERg3lASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYDQCABLQAAIABBvNQAai0AAEcNESAAQQFGDQIgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM5QELIAEgBEYEQEGgASECDOUBC0EBIAEtAABB3wBHDREaIAFBAWohAUGHASECDMsBCyADQQA2AgAgBkEBaiEBQYgBIQIMygELQZ8BIQIgASAERg3iASADKAIAIgAgBCABa2ohBSABIABrQQhqIQYCQANAIAEtAAAgAEGE1QBqLQAARw0PIABBCEYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAzjAQsgA0EANgIAIAZBAWohAUEpDA8LQZ4BIQIgASAERg3hASADKAIAIgAgBCABa2ohBSABIABrQQNqIQYCQANAIAEtAAAgAEG41ABqLQAARw0OIABBA0YNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAziAQsgA0EANgIAIAZBAWohAUEtDA4LIAEgBEYEQEGdASECDOEBCyABLQAAQcUARw0OIAFBAWohAUGEASECDMcBCyABIARGBEBBnAEhAgzgAQsCQAJAIAEtAABBzABrDggADw8PDw8PAQ8LIAFBAWohAUGCASECDMcBCyABQQFqIQFBgwEhAgzGAQtBmwEhAiABIARGDd4BIAMoAgAiACAEIAFraiEFIAEgAGtBBGohBgJAA0AgAS0AACAAQbPUAGotAABHDQsgAEEERg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADN8BCyADQQA2AgAgBkEBaiEBQSMMCwtBmgEhAiABIARGDd0BIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQbDUAGotAABHDQogAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADN4BCyADQQA2AgAgBkEBaiEBQQAMCgsgASAERgRAQZkBIQIM3QELAkACQCABLQAAQcgAaw4IAAwMDAwMDAEMCyABQQFqIQFB/QAhAgzEAQsgAUEBaiEBQYABIQIMwwELIAEgBEYEQEGYASECDNwBCwJAAkAgAS0AAEHOAGsOAwALAQsLIAFBAWohAUH+ACECDMMBCyABQQFqIQFB/wAhAgzCAQsgASAERgRAQZcBIQIM2wELIAEtAABB2QBHDQggAUEBaiEBQQgMBwtBlgEhAiABIARGDdkBIAMoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQazUAGotAABHDQYgAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNoBCyADQQA2AgAgBkEBaiEBQQUMBgtBlQEhAiABIARGDdgBIAMoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQabUAGotAABHDQUgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNkBCyADQQA2AgAgBkEBaiEBQRYMBQtBlAEhAiABIARGDdcBIAMoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQaHVAGotAABHDQQgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAyAFNgIADNgBCyADQQA2AgAgBkEBaiEBQRAMBAsgASAERgRAQZMBIQIM1wELAkACQCABLQAAQcMAaw4MAAYGBgYGBgYGBgYBBgsgAUEBaiEBQfkAIQIMvgELIAFBAWohAUH6ACECDL0BC0GSASECIAEgBEYN1QEgAygCACIAIAQgAWtqIQUgASAAa0EFaiEGAkADQCABLQAAIABBoNQAai0AAEcNAiAAQQVGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAM1gELIANBADYCACAGQQFqIQFBJAwCCyADQQA2AgAMAgsgASAERgRAQZEBIQIM1AELIAEtAABBzABHDQEgAUEBaiEBQRMLOgApIAMoAgQhACADQQA2AgQgAyAAIAEQLiIADQIMAQtBACECIANBADYCHCADIAE2AhQgA0H+HzYCECADQQY2AgwM0QELQfgAIQIMtwELIANBkAE2AhwgAyABNgIUIAMgADYCDEEAIQIMzwELQQAhAAJAIAMoAjgiAkUNACACKAJAIgJFDQAgAyACEQAAIQALIABFDQAgAEEVRg0BIANBADYCHCADIAE2AhQgA0GCDzYCECADQSA2AgxBACECDM4BC0H3ACECDLQBCyADQY8BNgIcIAMgATYCFCADQewbNgIQIANBFTYCDEEAIQIMzAELIAEgBEYEQEGPASECDMwBCwJAIAEtAABBIEYEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQZsfNgIQIANBBjYCDEEAIQIMzAELQQIhAgyyAQsDQCABLQAAQSBHDQIgBCABQQFqIgFHDQALQY4BIQIMygELIAEgBEYEQEGNASECDMoBCwJAIAEtAABBCWsOBEoAAEoAC0H1ACECDLABCyADLQApQQVGBEBB9gAhAgywAQtB9AAhAgyvAQsgASAERgRAQYwBIQIMyAELIANBEDYCCCADIAE2AgQMCgsgASAERgRAQYsBIQIMxwELAkAgAS0AAEEJaw4ERwAARwALQfMAIQIMrQELIAEgBEcEQCADQRA2AgggAyABNgIEQfEAIQIMrQELQYoBIQIMxQELAkAgASAERwRAA0AgAS0AAEGg0ABqLQAAIgBBA0cEQAJAIABBAWsOAkkABAtB8AAhAgyvAQsgBCABQQFqIgFHDQALQYgBIQIMxgELQYgBIQIMxQELIANBADYCHCADIAE2AhQgA0HbIDYCECADQQc2AgxBACECDMQBCyABIARGBEBBiQEhAgzEAQsCQAJAAkAgAS0AAEGg0gBqLQAAQQFrDgNGAgABC0HyACECDKwBCyADQQA2AhwgAyABNgIUIANBtBI2AhAgA0EHNgIMQQAhAgzEAQtB6gAhAgyqAQsgASAERwRAIAFBAWohAUHvACECDKoBC0GHASECDMIBCyAEIAEiAEYEQEGGASECDMIBCyAALQAAIgFBL0YEQCAAQQFqIQFB7gAhAgypAQsgAUEJayICQRdLDQEgACEBQQEgAnRBm4CABHENQQwBCyAEIAEiAEYEQEGFASECDMEBCyAALQAAQS9HDQAgAEEBaiEBDAMLQQAhAiADQQA2AhwgAyAANgIUIANB2yA2AhAgA0EHNgIMDL8BCwJAAkACQAJAAkADQCABLQAAQaDOAGotAAAiAEEFRwRAAkACQCAAQQFrDghHBQYHCAAEAQgLQesAIQIMrQELIAFBAWohAUHtACECDKwBCyAEIAFBAWoiAUcNAAtBhAEhAgzDAQsgAUEBagwUCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQdsANgIcIAMgATYCFCADIAA2AgxBACECDMEBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQd0ANgIcIAMgATYCFCADIAA2AgxBACECDMABCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNHiADQfoANgIcIAMgATYCFCADIAA2AgxBACECDL8BCyADQQA2AhwgAyABNgIUIANB+Q82AhAgA0EHNgIMQQAhAgy+AQsgASAERgRAQYMBIQIMvgELAkAgAS0AAEGgzgBqLQAAQQFrDgg+BAUGAAgCAwcLIAFBAWohAQtBAyECDKMBCyABQQFqDA0LQQAhAiADQQA2AhwgA0HREjYCECADQQc2AgwgAyABQQFqNgIUDLoBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQdsANgIcIAMgATYCFCADIAA2AgxBACECDLkBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQd0ANgIcIAMgATYCFCADIAA2AgxBACECDLgBCyADKAIEIQAgA0EANgIEIAMgACABECwiAEUNFiADQfoANgIcIAMgATYCFCADIAA2AgxBACECDLcBCyADQQA2AhwgAyABNgIUIANB+Q82AhAgA0EHNgIMQQAhAgy2AQtB7AAhAgycAQsgASAERgRAQYIBIQIMtQELIAFBAWoMAgsgASAERgRAQYEBIQIMtAELIAFBAWoMAQsgASAERg0BIAFBAWoLIQFBBCECDJgBC0GAASECDLABCwNAIAEtAABBoMwAai0AACIAQQJHBEAgAEEBRwRAQekAIQIMmQELDDELIAQgAUEBaiIBRw0AC0H/ACECDK8BCyABIARGBEBB/gAhAgyvAQsCQCABLQAAQQlrDjcvAwYvBAYGBgYGBgYGBgYGBgYGBgYGBgUGBgIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYABgsgAUEBagshAUEFIQIMlAELIAFBAWoMBgsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgyrAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgyqAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQggA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgypAQsgA0EANgIcIAMgATYCFCADQY0UNgIQIANBBzYCDEEAIQIMqAELAkACQAJAAkADQCABLQAAQaDKAGotAAAiAEEFRwRAAkAgAEEBaw4GLgMEBQYABgtB6AAhAgyUAQsgBCABQQFqIgFHDQALQf0AIQIMqwELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB2wA2AhwgAyABNgIUIAMgADYCDEEAIQIMqgELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB3QA2AhwgAyABNgIUIAMgADYCDEEAIQIMqQELIAMoAgQhACADQQA2AgQgAyAAIAEQLCIARQ0HIANB+gA2AhwgAyABNgIUIAMgADYCDEEAIQIMqAELIANBADYCHCADIAE2AhQgA0HkCDYCECADQQc2AgxBACECDKcBCyABIARGDQEgAUEBagshAUEGIQIMjAELQfwAIQIMpAELAkACQAJAAkADQCABLQAAQaDIAGotAAAiAEEFRwRAIABBAWsOBCkCAwQFCyAEIAFBAWoiAUcNAAtB+wAhAgynAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0HbADYCHCADIAE2AhQgAyAANgIMQQAhAgymAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0HdADYCHCADIAE2AhQgAyAANgIMQQAhAgylAQsgAygCBCEAIANBADYCBCADIAAgARAsIgBFDQMgA0H6ADYCHCADIAE2AhQgAyAANgIMQQAhAgykAQsgA0EANgIcIAMgATYCFCADQbwKNgIQIANBBzYCDEEAIQIMowELQc8AIQIMiQELQdEAIQIMiAELQecAIQIMhwELIAEgBEYEQEH6ACECDKABCwJAIAEtAABBCWsOBCAAACAACyABQQFqIQFB5gAhAgyGAQsgASAERgRAQfkAIQIMnwELAkAgAS0AAEEJaw4EHwAAHwALQQAhAAJAIAMoAjgiAkUNACACKAI4IgJFDQAgAyACEQAAIQALIABFBEBB4gEhAgyGAQsgAEEVRwRAIANBADYCHCADIAE2AhQgA0HJDTYCECADQRo2AgxBACECDJ8BCyADQfgANgIcIAMgATYCFCADQeoaNgIQIANBFTYCDEEAIQIMngELIAEgBEcEQCADQQ02AgggAyABNgIEQeQAIQIMhQELQfcAIQIMnQELIAEgBEYEQEH2ACECDJ0BCwJAAkACQCABLQAAQcgAaw4LAAELCwsLCwsLCwILCyABQQFqIQFB3QAhAgyFAQsgAUEBaiEBQeAAIQIMhAELIAFBAWohAUHjACECDIMBC0H1ACECIAEgBEYNmwEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBtdUAai0AAEcNCCAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMnAELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgAEQCADQfQANgIcIAMgATYCFCADIAA2AgxBACECDJwBC0HiACECDIIBC0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMnAELQeEAIQIMggELIANB8wA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyaAQsgAy0AKSIAQSNrQQtJDQkCQCAAQQZLDQBBASAAdEHKAHFFDQAMCgtBACECIANBADYCHCADIAE2AhQgA0HtCTYCECADQQg2AgwMmQELQfIAIQIgASAERg2YASADKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEGz1QBqLQAARw0FIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAyZAQsgAygCBCEAIANCADcDACADIAAgBkEBaiIBECsiAARAIANB8QA2AhwgAyABNgIUIAMgADYCDEEAIQIMmQELQd8AIQIMfwtBACEAAkAgAygCOCICRQ0AIAIoAjQiAkUNACADIAIRAAAhAAsCQCAABEAgAEEVRg0BIANBADYCHCADIAE2AhQgA0HqDTYCECADQSY2AgxBACECDJkBC0HeACECDH8LIANB8AA2AhwgAyABNgIUIANBgBs2AhAgA0EVNgIMQQAhAgyXAQsgAy0AKUEhRg0GIANBADYCHCADIAE2AhQgA0GRCjYCECADQQg2AgxBACECDJYBC0HvACECIAEgBEYNlQEgAygCACIAIAQgAWtqIQUgASAAa0ECaiEGAkADQCABLQAAIABBsNUAai0AAEcNAiAAQQJGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyADIAU2AgAMlgELIAMoAgQhACADQgA3AwAgAyAAIAZBAWoiARArIgBFDQIgA0HtADYCHCADIAE2AhQgAyAANgIMQQAhAgyVAQsgA0EANgIACyADKAIEIQAgA0EANgIEIAMgACABECsiAEUNgAEgA0HuADYCHCADIAE2AhQgAyAANgIMQQAhAgyTAQtB3AAhAgx5C0EAIQACQCADKAI4IgJFDQAgAigCNCICRQ0AIAMgAhEAACEACwJAIAAEQCAAQRVGDQEgA0EANgIcIAMgATYCFCADQeoNNgIQIANBJjYCDEEAIQIMkwELQdsAIQIMeQsgA0HsADYCHCADIAE2AhQgA0GAGzYCECADQRU2AgxBACECDJEBCyADLQApIgBBI0kNACAAQS5GDQAgA0EANgIcIAMgATYCFCADQckJNgIQIANBCDYCDEEAIQIMkAELQdoAIQIMdgsgASAERgRAQesAIQIMjwELAkAgAS0AAEEvRgRAIAFBAWohAQwBCyADQQA2AhwgAyABNgIUIANBsjg2AhAgA0EINgIMQQAhAgyPAQtB2QAhAgx1CyABIARHBEAgA0EONgIIIAMgATYCBEHYACECDHULQeoAIQIMjQELIAEgBEYEQEHpACECDI0BCyABLQAAQTBrIgBB/wFxQQpJBEAgAyAAOgAqIAFBAWohAUHXACECDHQLIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ16IANB6AA2AhwgAyABNgIUIAMgADYCDEEAIQIMjAELIAEgBEYEQEHnACECDIwBCwJAIAEtAABBLkYEQCABQQFqIQEMAQsgAygCBCEAIANBADYCBCADIAAgARAvIgBFDXsgA0HmADYCHCADIAE2AhQgAyAANgIMQQAhAgyMAQtB1gAhAgxyCyABIARGBEBB5QAhAgyLAQtBACEAQQEhBUEBIQdBACECAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkAgAS0AAEEwaw4KCgkAAQIDBAUGCAsLQQIMBgtBAwwFC0EEDAQLQQUMAwtBBgwCC0EHDAELQQgLIQJBACEFQQAhBwwCC0EJIQJBASEAQQAhBUEAIQcMAQtBACEFQQEhAgsgAyACOgArIAFBAWohAQJAAkAgAy0ALkEQcQ0AAkACQAJAIAMtACoOAwEAAgQLIAdFDQMMAgsgAA0BDAILIAVFDQELIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ0CIANB4gA2AhwgAyABNgIUIAMgADYCDEEAIQIMjQELIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ19IANB4wA2AhwgAyABNgIUIAMgADYCDEEAIQIMjAELIAMoAgQhACADQQA2AgQgAyAAIAEQLyIARQ17IANB5AA2AhwgAyABNgIUIAMgADYCDAyLAQtB1AAhAgxxCyADLQApQSJGDYYBQdMAIQIMcAtBACEAAkAgAygCOCICRQ0AIAIoAkQiAkUNACADIAIRAAAhAAsgAEUEQEHVACECDHALIABBFUcEQCADQQA2AhwgAyABNgIUIANBpA02AhAgA0EhNgIMQQAhAgyJAQsgA0HhADYCHCADIAE2AhQgA0HQGjYCECADQRU2AgxBACECDIgBCyABIARGBEBB4AAhAgyIAQsCQAJAAkACQAJAIAEtAABBCmsOBAEEBAAECyABQQFqIQEMAQsgAUEBaiEBIANBL2otAABBAXFFDQELQdIAIQIMcAsgA0EANgIcIAMgATYCFCADQbYRNgIQIANBCTYCDEEAIQIMiAELIANBADYCHCADIAE2AhQgA0G2ETYCECADQQk2AgxBACECDIcBCyABIARGBEBB3wAhAgyHAQsgAS0AAEEKRgRAIAFBAWohAQwJCyADLQAuQcAAcQ0IIANBADYCHCADIAE2AhQgA0G2ETYCECADQQI2AgxBACECDIYBCyABIARGBEBB3QAhAgyGAQsgAS0AACICQQ1GBEAgAUEBaiEBQdAAIQIMbQsgASEAIAJBCWsOBAUBAQUBCyAEIAEiAEYEQEHcACECDIUBCyAALQAAQQpHDQAgAEEBagwCC0EAIQIgA0EANgIcIAMgADYCFCADQcotNgIQIANBBzYCDAyDAQsgASAERgRAQdsAIQIMgwELAkAgAS0AAEEJaw4EAwAAAwALIAFBAWoLIQFBzgAhAgxoCyABIARGBEBB2gAhAgyBAQsgAS0AAEEJaw4EAAEBAAELQQAhAiADQQA2AhwgA0GaEjYCECADQQc2AgwgAyABQQFqNgIUDH8LIANBgBI7ASpBACEAAkAgAygCOCICRQ0AIAIoAjgiAkUNACADIAIRAAAhAAsgAEUNACAAQRVHDQEgA0HZADYCHCADIAE2AhQgA0HqGjYCECADQRU2AgxBACECDH4LQc0AIQIMZAsgA0EANgIcIAMgATYCFCADQckNNgIQIANBGjYCDEEAIQIMfAsgASAERgRAQdkAIQIMfAsgAS0AAEEgRw09IAFBAWohASADLQAuQQFxDT0gA0EANgIcIAMgATYCFCADQcIcNgIQIANBHjYCDEEAIQIMewsgASAERgRAQdgAIQIMewsCQAJAAkACQAJAIAEtAAAiAEEKaw4EAgMDAAELIAFBAWohAUEsIQIMZQsgAEE6Rw0BIANBADYCHCADIAE2AhQgA0HnETYCECADQQo2AgxBACECDH0LIAFBAWohASADQS9qLQAAQQFxRQ1zIAMtADJBgAFxRQRAIANBMmohAiADEDVBACEAAkAgAygCOCIGRQ0AIAYoAigiBkUNACADIAYRAAAhAAsCQAJAIAAOFk1MSwEBAQEBAQEBAQEBAQEBAQEBAQABCyADQSk2AhwgAyABNgIUIANBrBk2AhAgA0EVNgIMQQAhAgx+CyADQQA2AhwgAyABNgIUIANB5Qs2AhAgA0ERNgIMQQAhAgx9C0EAIQACQCADKAI4IgJFDQAgAigCXCICRQ0AIAMgAhEAACEACyAARQ1ZIABBFUcNASADQQU2AhwgAyABNgIUIANBmxs2AhAgA0EVNgIMQQAhAgx8C0HLACECDGILQQAhAiADQQA2AhwgAyABNgIUIANBkA42AhAgA0EUNgIMDHoLIAMgAy8BMkGAAXI7ATIMOwsgASAERwRAIANBETYCCCADIAE2AgRBygAhAgxgC0HXACECDHgLIAEgBEYEQEHWACECDHgLAkACQAJAAkAgAS0AACIAQSByIAAgAEHBAGtB/wFxQRpJG0H/AXFB4wBrDhMAQEBAQEBAQEBAQEBAAUBAQAIDQAsgAUEBaiEBQcYAIQIMYQsgAUEBaiEBQccAIQIMYAsgAUEBaiEBQcgAIQIMXwsgAUEBaiEBQckAIQIMXgtB1QAhAiAEIAEiAEYNdiAEIAFrIAMoAgAiAWohBiAAIAFrQQVqIQcDQCABQZDIAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQhBBCABQQVGDQoaIAFBAWohASAEIABBAWoiAEcNAAsgAyAGNgIADHYLQdQAIQIgBCABIgBGDXUgBCABayADKAIAIgFqIQYgACABa0EPaiEHA0AgAUGAyABqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0HQQMgAUEPRg0JGiABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAx1C0HTACECIAQgASIARg10IAQgAWsgAygCACIBaiEGIAAgAWtBDmohBwNAIAFB4scAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNBiABQQ5GDQcgAUEBaiEBIAQgAEEBaiIARw0ACyADIAY2AgAMdAtB0gAhAiAEIAEiAEYNcyAEIAFrIAMoAgAiAWohBSAAIAFrQQFqIQYDQCABQeDHAGotAAAgAC0AACIHQSByIAcgB0HBAGtB/wFxQRpJG0H/AXFHDQUgAUEBRg0CIAFBAWohASAEIABBAWoiAEcNAAsgAyAFNgIADHMLIAEgBEYEQEHRACECDHMLAkACQCABLQAAIgBBIHIgACAAQcEAa0H/AXFBGkkbQf8BcUHuAGsOBwA5OTk5OQE5CyABQQFqIQFBwwAhAgxaCyABQQFqIQFBxAAhAgxZCyADQQA2AgAgBkEBaiEBQcUAIQIMWAtB0AAhAiAEIAEiAEYNcCAEIAFrIAMoAgAiAWohBiAAIAFrQQlqIQcDQCABQdbHAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQJBAiABQQlGDQQaIAFBAWohASAEIABBAWoiAEcNAAsgAyAGNgIADHALQc8AIQIgBCABIgBGDW8gBCABayADKAIAIgFqIQYgACABa0EFaiEHA0AgAUHQxwBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0BIAFBBUYNAiABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAxvCyAAIQEgA0EANgIADDMLQQELOgAsIANBADYCACAHQQFqIQELQS0hAgxSCwJAA0AgAS0AAEHQxQBqLQAAQQFHDQEgBCABQQFqIgFHDQALQc0AIQIMawtBwgAhAgxRCyABIARGBEBBzAAhAgxqCyABLQAAQTpGBEAgAygCBCEAIANBADYCBCADIAAgARAwIgBFDTMgA0HLADYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgxqCyADQQA2AhwgAyABNgIUIANB5xE2AhAgA0EKNgIMQQAhAgxpCwJAAkAgAy0ALEECaw4CAAEnCyADQTNqLQAAQQJxRQ0mIAMtAC5BAnENJiADQQA2AhwgAyABNgIUIANBphQ2AhAgA0ELNgIMQQAhAgxpCyADLQAyQSBxRQ0lIAMtAC5BAnENJSADQQA2AhwgAyABNgIUIANBvRM2AhAgA0EPNgIMQQAhAgxoC0EAIQACQCADKAI4IgJFDQAgAigCSCICRQ0AIAMgAhEAACEACyAARQRAQcEAIQIMTwsgAEEVRwRAIANBADYCHCADIAE2AhQgA0GmDzYCECADQRw2AgxBACECDGgLIANBygA2AhwgAyABNgIUIANBhRw2AhAgA0EVNgIMQQAhAgxnCyABIARHBEAgASECA0AgBCACIgFrQRBOBEAgAUEQaiEC/Qz/////////////////////IAH9AAAAIg1BB/1sIA39DODg4ODg4ODg4ODg4ODg4OD9bv0MX19fX19fX19fX19fX19fX/0mIA39DAkJCQkJCQkJCQkJCQkJCQn9I/1Q/VL9ZEF/c2giAEEQRg0BIAAgAWohAQwYCyABIARGBEBBxAAhAgxpCyABLQAAQcDBAGotAABBAUcNFyAEIAFBAWoiAkcNAAtBxAAhAgxnC0HEACECDGYLIAEgBEcEQANAAkAgAS0AACIAQSByIAAgAEHBAGtB/wFxQRpJG0H/AXEiAEEJRg0AIABBIEYNAAJAAkACQAJAIABB4wBrDhMAAwMDAwMDAwEDAwMDAwMDAwMCAwsgAUEBaiEBQTYhAgxSCyABQQFqIQFBNyECDFELIAFBAWohAUE4IQIMUAsMFQsgBCABQQFqIgFHDQALQTwhAgxmC0E8IQIMZQsgASAERgRAQcgAIQIMZQsgA0ESNgIIIAMgATYCBAJAAkACQAJAAkAgAy0ALEEBaw4EFAABAgkLIAMtADJBIHENA0HgASECDE8LAkAgAy8BMiIAQQhxRQ0AIAMtAChBAUcNACADLQAuQQhxRQ0CCyADIABB9/sDcUGABHI7ATIMCwsgAyADLwEyQRByOwEyDAQLIANBADYCBCADIAEgARAxIgAEQCADQcEANgIcIAMgADYCDCADIAFBAWo2AhRBACECDGYLIAFBAWohAQxYCyADQQA2AhwgAyABNgIUIANB9BM2AhAgA0EENgIMQQAhAgxkC0HHACECIAEgBEYNYyADKAIAIgAgBCABa2ohBSABIABrQQZqIQYCQANAIABBwMUAai0AACABLQAAQSByRw0BIABBBkYNSiAAQQFqIQAgBCABQQFqIgFHDQALIAMgBTYCAAxkCyADQQA2AgAMBQsCQCABIARHBEADQCABLQAAQcDDAGotAAAiAEEBRwRAIABBAkcNAyABQQFqIQEMBQsgBCABQQFqIgFHDQALQcUAIQIMZAtBxQAhAgxjCwsgA0EAOgAsDAELQQshAgxHC0E/IQIMRgsCQAJAA0AgAS0AACIAQSBHBEACQCAAQQprDgQDBQUDAAsgAEEsRg0DDAQLIAQgAUEBaiIBRw0AC0HGACECDGALIANBCDoALAwOCyADLQAoQQFHDQIgAy0ALkEIcQ0CIAMoAgQhACADQQA2AgQgAyAAIAEQMSIABEAgA0HCADYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgxfCyABQQFqIQEMUAtBOyECDEQLAkADQCABLQAAIgBBIEcgAEEJR3ENASAEIAFBAWoiAUcNAAtBwwAhAgxdCwtBPCECDEILAkACQCABIARHBEADQCABLQAAIgBBIEcEQCAAQQprDgQDBAQDBAsgBCABQQFqIgFHDQALQT8hAgxdC0E/IQIMXAsgAyADLwEyQSByOwEyDAoLIAMoAgQhACADQQA2AgQgAyAAIAEQMSIARQ1OIANBPjYCHCADIAE2AhQgAyAANgIMQQAhAgxaCwJAIAEgBEcEQANAIAEtAABBwMMAai0AACIAQQFHBEAgAEECRg0DDAwLIAQgAUEBaiIBRw0AC0E3IQIMWwtBNyECDFoLIAFBAWohAQwEC0E7IQIgBCABIgBGDVggBCABayADKAIAIgFqIQYgACABa0EFaiEHAkADQCABQZDIAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQEgAUEFRgRAQQchAQw/CyABQQFqIQEgBCAAQQFqIgBHDQALIAMgBjYCAAxZCyADQQA2AgAgACEBDAULQTohAiAEIAEiAEYNVyAEIAFrIAMoAgAiAWohBiAAIAFrQQhqIQcCQANAIAFBtMEAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNASABQQhGBEBBBSEBDD4LIAFBAWohASAEIABBAWoiAEcNAAsgAyAGNgIADFgLIANBADYCACAAIQEMBAtBOSECIAQgASIARg1WIAQgAWsgAygCACIBaiEGIAAgAWtBA2ohBwJAA0AgAUGwwQBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0BIAFBA0YEQEEGIQEMPQsgAUEBaiEBIAQgAEEBaiIARw0ACyADIAY2AgAMVwsgA0EANgIAIAAhAQwDCwJAA0AgAS0AACIAQSBHBEAgAEEKaw4EBwQEBwILIAQgAUEBaiIBRw0AC0E4IQIMVgsgAEEsRw0BIAFBAWohAEEBIQECQAJAAkACQAJAIAMtACxBBWsOBAMBAgQACyAAIQEMBAtBAiEBDAELQQQhAQsgA0EBOgAsIAMgAy8BMiABcjsBMiAAIQEMAQsgAyADLwEyQQhyOwEyIAAhAQtBPiECDDsLIANBADoALAtBOSECDDkLIAEgBEYEQEE2IQIMUgsCQAJAAkACQAJAIAEtAABBCmsOBAACAgECCyADKAIEIQAgA0EANgIEIAMgACABEDEiAEUNAiADQTM2AhwgAyABNgIUIAMgADYCDEEAIQIMVQsgAygCBCEAIANBADYCBCADIAAgARAxIgBFBEAgAUEBaiEBDAYLIANBMjYCHCADIAA2AgwgAyABQQFqNgIUQQAhAgxUCyADLQAuQQFxBEBB3wEhAgw7CyADKAIEIQAgA0EANgIEIAMgACABEDEiAA0BDEkLQTQhAgw5CyADQTU2AhwgAyABNgIUIAMgADYCDEEAIQIMUQtBNSECDDcLIANBL2otAABBAXENACADQQA2AhwgAyABNgIUIANB6xY2AhAgA0EZNgIMQQAhAgxPC0EzIQIMNQsgASAERgRAQTIhAgxOCwJAIAEtAABBCkYEQCABQQFqIQEMAQsgA0EANgIcIAMgATYCFCADQZIXNgIQIANBAzYCDEEAIQIMTgtBMiECDDQLIAEgBEYEQEExIQIMTQsCQCABLQAAIgBBCUYNACAAQSBGDQBBASECAkAgAy0ALEEFaw4EBgQFAA0LIAMgAy8BMkEIcjsBMgwMCyADLQAuQQFxRQ0BIAMtACxBCEcNACADQQA6ACwLQT0hAgwyCyADQQA2AhwgAyABNgIUIANBwhY2AhAgA0EKNgIMQQAhAgxKC0ECIQIMAQtBBCECCyADQQE6ACwgAyADLwEyIAJyOwEyDAYLIAEgBEYEQEEwIQIMRwsgAS0AAEEKRgRAIAFBAWohAQwBCyADLQAuQQFxDQAgA0EANgIcIAMgATYCFCADQdwoNgIQIANBAjYCDEEAIQIMRgtBMCECDCwLIAFBAWohAUExIQIMKwsgASAERgRAQS8hAgxECyABLQAAIgBBCUcgAEEgR3FFBEAgAUEBaiEBIAMtAC5BAXENASADQQA2AhwgAyABNgIUIANBlxA2AhAgA0EKNgIMQQAhAgxEC0EBIQICQAJAAkACQAJAAkAgAy0ALEECaw4HBQQEAwECAAQLIAMgAy8BMkEIcjsBMgwDC0ECIQIMAQtBBCECCyADQQE6ACwgAyADLwEyIAJyOwEyC0EvIQIMKwsgA0EANgIcIAMgATYCFCADQYQTNgIQIANBCzYCDEEAIQIMQwtB4QEhAgwpCyABIARGBEBBLiECDEILIANBADYCBCADQRI2AgggAyABIAEQMSIADQELQS4hAgwnCyADQS02AhwgAyABNgIUIAMgADYCDEEAIQIMPwtBACEAAkAgAygCOCICRQ0AIAIoAkwiAkUNACADIAIRAAAhAAsgAEUNACAAQRVHDQEgA0HYADYCHCADIAE2AhQgA0GzGzYCECADQRU2AgxBACECDD4LQcwAIQIMJAsgA0EANgIcIAMgATYCFCADQbMONgIQIANBHTYCDEEAIQIMPAsgASAERgRAQc4AIQIMPAsgAS0AACIAQSBGDQIgAEE6Rg0BCyADQQA6ACxBCSECDCELIAMoAgQhACADQQA2AgQgAyAAIAEQMCIADQEMAgsgAy0ALkEBcQRAQd4BIQIMIAsgAygCBCEAIANBADYCBCADIAAgARAwIgBFDQIgA0EqNgIcIAMgADYCDCADIAFBAWo2AhRBACECDDgLIANBywA2AhwgAyAANgIMIAMgAUEBajYCFEEAIQIMNwsgAUEBaiEBQcAAIQIMHQsgAUEBaiEBDCwLIAEgBEYEQEErIQIMNQsCQCABLQAAQQpGBEAgAUEBaiEBDAELIAMtAC5BwABxRQ0GCyADLQAyQYABcQRAQQAhAAJAIAMoAjgiAkUNACACKAJcIgJFDQAgAyACEQAAIQALIABFDRIgAEEVRgRAIANBBTYCHCADIAE2AhQgA0GbGzYCECADQRU2AgxBACECDDYLIANBADYCHCADIAE2AhQgA0GQDjYCECADQRQ2AgxBACECDDULIANBMmohAiADEDVBACEAAkAgAygCOCIGRQ0AIAYoAigiBkUNACADIAYRAAAhAAsgAA4WAgEABAQEBAQEBAQEBAQEBAQEBAQEAwQLIANBAToAMAsgAiACLwEAQcAAcjsBAAtBKyECDBgLIANBKTYCHCADIAE2AhQgA0GsGTYCECADQRU2AgxBACECDDALIANBADYCHCADIAE2AhQgA0HlCzYCECADQRE2AgxBACECDC8LIANBADYCHCADIAE2AhQgA0GlCzYCECADQQI2AgxBACECDC4LQQEhByADLwEyIgVBCHFFBEAgAykDIEIAUiEHCwJAIAMtADAEQEEBIQAgAy0AKUEFRg0BIAVBwABxRSAHcUUNAQsCQCADLQAoIgJBAkYEQEEBIQAgAy8BNCIGQeUARg0CQQAhACAFQcAAcQ0CIAZB5ABGDQIgBkHmAGtBAkkNAiAGQcwBRg0CIAZBsAJGDQIMAQtBACEAIAVBwABxDQELQQIhACAFQQhxDQAgBUGABHEEQAJAIAJBAUcNACADLQAuQQpxDQBBBSEADAILQQQhAAwBCyAFQSBxRQRAIAMQNkEAR0ECdCEADAELQQBBAyADKQMgUBshAAsgAEEBaw4FAgAHAQMEC0ERIQIMEwsgA0EBOgAxDCkLQQAhAgJAIAMoAjgiAEUNACAAKAIwIgBFDQAgAyAAEQAAIQILIAJFDSYgAkEVRgRAIANBAzYCHCADIAE2AhQgA0HSGzYCECADQRU2AgxBACECDCsLQQAhAiADQQA2AhwgAyABNgIUIANB3Q42AhAgA0ESNgIMDCoLIANBADYCHCADIAE2AhQgA0H5IDYCECADQQ82AgxBACECDCkLQQAhAAJAIAMoAjgiAkUNACACKAIwIgJFDQAgAyACEQAAIQALIAANAQtBDiECDA4LIABBFUYEQCADQQI2AhwgAyABNgIUIANB0hs2AhAgA0EVNgIMQQAhAgwnCyADQQA2AhwgAyABNgIUIANB3Q42AhAgA0ESNgIMQQAhAgwmC0EqIQIMDAsgASAERwRAIANBCTYCCCADIAE2AgRBKSECDAwLQSYhAgwkCyADIAMpAyAiDCAEIAFrrSIKfSILQgAgCyAMWBs3AyAgCiAMVARAQSUhAgwkCyADKAIEIQAgA0EANgIEIAMgACABIAynaiIBEDIiAEUNACADQQU2AhwgAyABNgIUIAMgADYCDEEAIQIMIwtBDyECDAkLQgAhCgJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCABLQAAQTBrDjcXFgABAgMEBQYHFBQUFBQUFAgJCgsMDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUDg8QERITFAtCAiEKDBYLQgMhCgwVC0IEIQoMFAtCBSEKDBMLQgYhCgwSC0IHIQoMEQtCCCEKDBALQgkhCgwPC0IKIQoMDgtCCyEKDA0LQgwhCgwMC0INIQoMCwtCDiEKDAoLQg8hCgwJC0IKIQoMCAtCCyEKDAcLQgwhCgwGC0INIQoMBQtCDiEKDAQLQg8hCgwDCyADQQA2AhwgAyABNgIUIANBnxU2AhAgA0EMNgIMQQAhAgwhCyABIARGBEBBIiECDCELQgAhCgJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAS0AAEEwaw43FRQAAQIDBAUGBxYWFhYWFhYICQoLDA0WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFg4PEBESExYLQgIhCgwUC0IDIQoMEwtCBCEKDBILQgUhCgwRC0IGIQoMEAtCByEKDA8LQgghCgwOC0IJIQoMDQtCCiEKDAwLQgshCgwLC0IMIQoMCgtCDSEKDAkLQg4hCgwIC0IPIQoMBwtCCiEKDAYLQgshCgwFC0IMIQoMBAtCDSEKDAMLQg4hCgwCC0IPIQoMAQtCASEKCyABQQFqIQEgAykDICILQv//////////D1gEQCADIAtCBIYgCoQ3AyAMAgsgA0EANgIcIAMgATYCFCADQbUJNgIQIANBDDYCDEEAIQIMHgtBJyECDAQLQSghAgwDCyADIAE6ACwgA0EANgIAIAdBAWohAUEMIQIMAgsgA0EANgIAIAZBAWohAUEKIQIMAQsgAUEBaiEBQQghAgwACwALQQAhAiADQQA2AhwgAyABNgIUIANBsjg2AhAgA0EINgIMDBcLQQAhAiADQQA2AhwgAyABNgIUIANBgxE2AhAgA0EJNgIMDBYLQQAhAiADQQA2AhwgAyABNgIUIANB3wo2AhAgA0EJNgIMDBULQQAhAiADQQA2AhwgAyABNgIUIANB7RA2AhAgA0EJNgIMDBQLQQAhAiADQQA2AhwgAyABNgIUIANB0hE2AhAgA0EJNgIMDBMLQQAhAiADQQA2AhwgAyABNgIUIANBsjg2AhAgA0EINgIMDBILQQAhAiADQQA2AhwgAyABNgIUIANBgxE2AhAgA0EJNgIMDBELQQAhAiADQQA2AhwgAyABNgIUIANB3wo2AhAgA0EJNgIMDBALQQAhAiADQQA2AhwgAyABNgIUIANB7RA2AhAgA0EJNgIMDA8LQQAhAiADQQA2AhwgAyABNgIUIANB0hE2AhAgA0EJNgIMDA4LQQAhAiADQQA2AhwgAyABNgIUIANBuRc2AhAgA0EPNgIMDA0LQQAhAiADQQA2AhwgAyABNgIUIANBuRc2AhAgA0EPNgIMDAwLQQAhAiADQQA2AhwgAyABNgIUIANBmRM2AhAgA0ELNgIMDAsLQQAhAiADQQA2AhwgAyABNgIUIANBnQk2AhAgA0ELNgIMDAoLQQAhAiADQQA2AhwgAyABNgIUIANBlxA2AhAgA0EKNgIMDAkLQQAhAiADQQA2AhwgAyABNgIUIANBsRA2AhAgA0EKNgIMDAgLQQAhAiADQQA2AhwgAyABNgIUIANBux02AhAgA0ECNgIMDAcLQQAhAiADQQA2AhwgAyABNgIUIANBlhY2AhAgA0ECNgIMDAYLQQAhAiADQQA2AhwgAyABNgIUIANB+Rg2AhAgA0ECNgIMDAULQQAhAiADQQA2AhwgAyABNgIUIANBxBg2AhAgA0ECNgIMDAQLIANBAjYCHCADIAE2AhQgA0GpHjYCECADQRY2AgxBACECDAMLQd4AIQIgASAERg0CIAlBCGohByADKAIAIQUCQAJAIAEgBEcEQCAFQZbIAGohCCAEIAVqIAFrIQYgBUF/c0EKaiIFIAFqIQADQCABLQAAIAgtAABHBEBBAiEIDAMLIAVFBEBBACEIIAAhAQwDCyAFQQFrIQUgCEEBaiEIIAQgAUEBaiIBRw0ACyAGIQUgBCEBCyAHQQE2AgAgAyAFNgIADAELIANBADYCACAHIAg2AgALIAcgATYCBCAJKAIMIQACQAJAIAkoAghBAWsOAgQBAAsgA0EANgIcIANBwh42AhAgA0EXNgIMIAMgAEEBajYCFEEAIQIMAwsgA0EANgIcIAMgADYCFCADQdceNgIQIANBCTYCDEEAIQIMAgsgASAERgRAQSghAgwCCyADQQk2AgggAyABNgIEQSchAgwBCyABIARGBEBBASECDAELA0ACQAJAAkAgAS0AAEEKaw4EAAEBAAELIAFBAWohAQwBCyABQQFqIQEgAy0ALkEgcQ0AQQAhAiADQQA2AhwgAyABNgIUIANBoSE2AhAgA0EFNgIMDAILQQEhAiABIARHDQALCyAJQRBqJAAgAkUEQCADKAIMIQAMAQsgAyACNgIcQQAhACADKAIEIgFFDQAgAyABIAQgAygCCBEBACIBRQ0AIAMgBDYCFCADIAE2AgwgASEACyAAC74CAQJ/IABBADoAACAAQeQAaiIBQQFrQQA6AAAgAEEAOgACIABBADoAASABQQNrQQA6AAAgAUECa0EAOgAAIABBADoAAyABQQRrQQA6AABBACAAa0EDcSIBIABqIgBBADYCAEHkACABa0F8cSICIABqIgFBBGtBADYCAAJAIAJBCUkNACAAQQA2AgggAEEANgIEIAFBCGtBADYCACABQQxrQQA2AgAgAkEZSQ0AIABBADYCGCAAQQA2AhQgAEEANgIQIABBADYCDCABQRBrQQA2AgAgAUEUa0EANgIAIAFBGGtBADYCACABQRxrQQA2AgAgAiAAQQRxQRhyIgJrIgFBIEkNACAAIAJqIQADQCAAQgA3AxggAEIANwMQIABCADcDCCAAQgA3AwAgAEEgaiEAIAFBIGsiAUEfSw0ACwsLVgEBfwJAIAAoAgwNAAJAAkACQAJAIAAtADEOAwEAAwILIAAoAjgiAUUNACABKAIwIgFFDQAgACABEQAAIgENAwtBAA8LAAsgAEHKGTYCEEEOIQELIAELGgAgACgCDEUEQCAAQd4fNgIQIABBFTYCDAsLFAAgACgCDEEVRgRAIABBADYCDAsLFAAgACgCDEEWRgRAIABBADYCDAsLBwAgACgCDAsHACAAKAIQCwkAIAAgATYCEAsHACAAKAIUCysAAkAgAEEnTw0AQv//////CSAArYhCAYNQDQAgAEECdEHQOGooAgAPCwALFwAgAEEvTwRAAAsgAEECdEHsOWooAgALvwkBAX9B9C0hAQJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABB5ABrDvQDY2IAAWFhYWFhYQIDBAVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhBgcICQoLDA0OD2FhYWFhEGFhYWFhYWFhYWFhEWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYRITFBUWFxgZGhthYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2YTc4OTphYWFhYWFhYTthYWE8YWFhYT0+P2FhYWFhYWFhQGFhQWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYUJDREVGR0hJSktMTU5PUFFSU2FhYWFhYWFhVFVWV1hZWlthXF1hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFeYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhX2BhC0HqLA8LQZgmDwtB7TEPC0GgNw8LQckpDwtBtCkPC0GWLQ8LQesrDwtBojUPC0HbNA8LQeApDwtB4yQPC0HVJA8LQe4kDwtB5iUPC0HKNA8LQdA3DwtBqjUPC0H1LA8LQfYmDwtBgiIPC0HyMw8LQb4oDwtB5zcPC0HNIQ8LQcAhDwtBuCUPC0HLJQ8LQZYkDwtBjzQPC0HNNQ8LQd0qDwtB7jMPC0GcNA8LQZ4xDwtB9DUPC0HlIg8LQa8lDwtBmTEPC0GyNg8LQfk2DwtBxDIPC0HdLA8LQYIxDwtBwTEPC0GNNw8LQckkDwtB7DYPC0HnKg8LQcgjDwtB4iEPC0HJNw8LQaUiDwtBlCIPC0HbNg8LQd41DwtBhiYPC0G8Kw8LQYsyDwtBoCMPC0H2MA8LQYAsDwtBiSsPC0GkJg8LQfIjDwtBgSgPC0GrMg8LQesnDwtBwjYPC0GiJA8LQc8qDwtB3CMPC0GHJw8LQeQ0DwtBtyIPC0GtMQ8LQdUiDwtBrzQPC0HeJg8LQdYyDwtB9DQPC0GBOA8LQfQ3DwtBkjYPC0GdJw8LQYIpDwtBjSMPC0HXMQ8LQb01DwtBtDcPC0HYMA8LQbYnDwtBmjgPC0GnKg8LQcQnDwtBriMPC0H1Ig8LAAtByiYhAQsgAQsXACAAIAAvAS5B/v8DcSABQQBHcjsBLgsaACAAIAAvAS5B/f8DcSABQQBHQQF0cjsBLgsaACAAIAAvAS5B+/8DcSABQQBHQQJ0cjsBLgsaACAAIAAvAS5B9/8DcSABQQBHQQN0cjsBLgsaACAAIAAvAS5B7/8DcSABQQBHQQR0cjsBLgsaACAAIAAvAS5B3/8DcSABQQBHQQV0cjsBLgsaACAAIAAvAS5Bv/8DcSABQQBHQQZ0cjsBLgsaACAAIAAvAS5B//4DcSABQQBHQQd0cjsBLgsaACAAIAAvAS5B//0DcSABQQBHQQh0cjsBLgsaACAAIAAvAS5B//sDcSABQQBHQQl0cjsBLgs+AQJ/AkAgACgCOCIDRQ0AIAMoAgQiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQeESNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAggiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQfwRNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAgwiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQewKNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAhAiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQfoeNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAhQiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQcsQNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAhgiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQbcfNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAhwiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQb8VNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAiwiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQf4INgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAiAiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQYwdNgIQQRghBAsgBAs+AQJ/AkAgACgCOCIDRQ0AIAMoAiQiA0UNACAAIAEgAiABayADEQEAIgRBf0cNACAAQeYVNgIQQRghBAsgBAs4ACAAAn8gAC8BMkEUcUEURgRAQQEgAC0AKEEBRg0BGiAALwE0QeUARgwBCyAALQApQQVGCzoAMAtZAQJ/AkAgAC0AKEEBRg0AIAAvATQiAUHkAGtB5ABJDQAgAUHMAUYNACABQbACRg0AIAAvATIiAEHAAHENAEEBIQIgAEGIBHFBgARGDQAgAEEocUUhAgsgAguMAQECfwJAAkACQCAALQAqRQ0AIAAtACtFDQAgAC8BMiIBQQJxRQ0BDAILIAAvATIiAUEBcUUNAQtBASECIAAtAChBAUYNACAALwE0IgBB5ABrQeQASQ0AIABBzAFGDQAgAEGwAkYNACABQcAAcQ0AQQAhAiABQYgEcUGABEYNACABQShxQQBHIQILIAILcwAgAEEQav0MAAAAAAAAAAAAAAAAAAAAAP0LAwAgAP0MAAAAAAAAAAAAAAAAAAAAAP0LAwAgAEEwav0MAAAAAAAAAAAAAAAAAAAAAP0LAwAgAEEgav0MAAAAAAAAAAAAAAAAAAAAAP0LAwAgAEH9ATYCHAsGACAAEDoLmi0BC38jAEEQayIKJABB3NUAKAIAIglFBEBBnNkAKAIAIgVFBEBBqNkAQn83AgBBoNkAQoCAhICAgMAANwIAQZzZACAKQQhqQXBxQdiq1aoFcyIFNgIAQbDZAEEANgIAQYDZAEEANgIAC0GE2QBBwNkENgIAQdTVAEHA2QQ2AgBB6NUAIAU2AgBB5NUAQX82AgBBiNkAQcCmAzYCAANAIAFBgNYAaiABQfTVAGoiAjYCACACIAFB7NUAaiIDNgIAIAFB+NUAaiADNgIAIAFBiNYAaiABQfzVAGoiAzYCACADIAI2AgAgAUGQ1gBqIAFBhNYAaiICNgIAIAIgAzYCACABQYzWAGogAjYCACABQSBqIgFBgAJHDQALQczZBEGBpgM2AgBB4NUAQazZACgCADYCAEHQ1QBBgKYDNgIAQdzVAEHI2QQ2AgBBzP8HQTg2AgBByNkEIQkLAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAEHsAU0EQEHE1QAoAgAiBkEQIABBE2pBcHEgAEELSRsiBEEDdiIAdiIBQQNxBEACQCABQQFxIAByQQFzIgJBA3QiAEHs1QBqIgEgAEH01QBqKAIAIgAoAggiA0YEQEHE1QAgBkF+IAJ3cTYCAAwBCyABIAM2AgggAyABNgIMCyAAQQhqIQEgACACQQN0IgJBA3I2AgQgACACaiIAIAAoAgRBAXI2AgQMEQtBzNUAKAIAIgggBE8NASABBEACQEECIAB0IgJBACACa3IgASAAdHFoIgBBA3QiAkHs1QBqIgEgAkH01QBqKAIAIgIoAggiA0YEQEHE1QAgBkF+IAB3cSIGNgIADAELIAEgAzYCCCADIAE2AgwLIAIgBEEDcjYCBCAAQQN0IgAgBGshBSAAIAJqIAU2AgAgAiAEaiIEIAVBAXI2AgQgCARAIAhBeHFB7NUAaiEAQdjVACgCACEDAn9BASAIQQN2dCIBIAZxRQRAQcTVACABIAZyNgIAIAAMAQsgACgCCAsiASADNgIMIAAgAzYCCCADIAA2AgwgAyABNgIICyACQQhqIQFB2NUAIAQ2AgBBzNUAIAU2AgAMEQtByNUAKAIAIgtFDQEgC2hBAnRB9NcAaigCACIAKAIEQXhxIARrIQUgACECA0ACQCACKAIQIgFFBEAgAkEUaigCACIBRQ0BCyABKAIEQXhxIARrIgMgBUkhAiADIAUgAhshBSABIAAgAhshACABIQIMAQsLIAAoAhghCSAAKAIMIgMgAEcEQEHU1QAoAgAaIAMgACgCCCIBNgIIIAEgAzYCDAwQCyAAQRRqIgIoAgAiAUUEQCAAKAIQIgFFDQMgAEEQaiECCwNAIAIhByABIgNBFGoiAigCACIBDQAgA0EQaiECIAMoAhAiAQ0ACyAHQQA2AgAMDwtBfyEEIABBv39LDQAgAEETaiIBQXBxIQRByNUAKAIAIghFDQBBACAEayEFAkACQAJAAn9BACAEQYACSQ0AGkEfIARB////B0sNABogBEEmIAFBCHZnIgBrdkEBcSAAQQF0a0E+agsiBkECdEH01wBqKAIAIgJFBEBBACEBQQAhAwwBC0EAIQEgBEEZIAZBAXZrQQAgBkEfRxt0IQBBACEDA0ACQCACKAIEQXhxIARrIgcgBU8NACACIQMgByIFDQBBACEFIAIhAQwDCyABIAJBFGooAgAiByAHIAIgAEEddkEEcWpBEGooAgAiAkYbIAEgBxshASAAQQF0IQAgAg0ACwsgASADckUEQEEAIQNBAiAGdCIAQQAgAGtyIAhxIgBFDQMgAGhBAnRB9NcAaigCACEBCyABRQ0BCwNAIAEoAgRBeHEgBGsiAiAFSSEAIAIgBSAAGyEFIAEgAyAAGyEDIAEoAhAiAAR/IAAFIAFBFGooAgALIgENAAsLIANFDQAgBUHM1QAoAgAgBGtPDQAgAygCGCEHIAMgAygCDCIARwRAQdTVACgCABogACADKAIIIgE2AgggASAANgIMDA4LIANBFGoiAigCACIBRQRAIAMoAhAiAUUNAyADQRBqIQILA0AgAiEGIAEiAEEUaiICKAIAIgENACAAQRBqIQIgACgCECIBDQALIAZBADYCAAwNC0HM1QAoAgAiAyAETwRAQdjVACgCACEBAkAgAyAEayICQRBPBEAgASAEaiIAIAJBAXI2AgQgASADaiACNgIAIAEgBEEDcjYCBAwBCyABIANBA3I2AgQgASADaiIAIAAoAgRBAXI2AgRBACEAQQAhAgtBzNUAIAI2AgBB2NUAIAA2AgAgAUEIaiEBDA8LQdDVACgCACIDIARLBEAgBCAJaiIAIAMgBGsiAUEBcjYCBEHc1QAgADYCAEHQ1QAgATYCACAJIARBA3I2AgQgCUEIaiEBDA8LQQAhASAEAn9BnNkAKAIABEBBpNkAKAIADAELQajZAEJ/NwIAQaDZAEKAgISAgIDAADcCAEGc2QAgCkEMakFwcUHYqtWqBXM2AgBBsNkAQQA2AgBBgNkAQQA2AgBBgIAECyIAIARBxwBqIgVqIgZBACAAayIHcSICTwRAQbTZAEEwNgIADA8LAkBB/NgAKAIAIgFFDQBB9NgAKAIAIgggAmohACAAIAFNIAAgCEtxDQBBACEBQbTZAEEwNgIADA8LQYDZAC0AAEEEcQ0EAkACQCAJBEBBhNkAIQEDQCABKAIAIgAgCU0EQCAAIAEoAgRqIAlLDQMLIAEoAggiAQ0ACwtBABA7IgBBf0YNBSACIQZBoNkAKAIAIgFBAWsiAyAAcQRAIAIgAGsgACADakEAIAFrcWohBgsgBCAGTw0FIAZB/v///wdLDQVB/NgAKAIAIgMEQEH02AAoAgAiByAGaiEBIAEgB00NBiABIANLDQYLIAYQOyIBIABHDQEMBwsgBiADayAHcSIGQf7///8HSw0EIAYQOyEAIAAgASgCACABKAIEakYNAyAAIQELAkAgBiAEQcgAak8NACABQX9GDQBBpNkAKAIAIgAgBSAGa2pBACAAa3EiAEH+////B0sEQCABIQAMBwsgABA7QX9HBEAgACAGaiEGIAEhAAwHC0EAIAZrEDsaDAQLIAEiAEF/Rw0FDAMLQQAhAwwMC0EAIQAMCgsgAEF/Rw0CC0GA2QBBgNkAKAIAQQRyNgIACyACQf7///8HSw0BIAIQOyEAQQAQOyEBIABBf0YNASABQX9GDQEgACABTw0BIAEgAGsiBiAEQThqTQ0BC0H02ABB9NgAKAIAIAZqIgE2AgBB+NgAKAIAIAFJBEBB+NgAIAE2AgALAkACQAJAQdzVACgCACICBEBBhNkAIQEDQCAAIAEoAgAiAyABKAIEIgVqRg0CIAEoAggiAQ0ACwwCC0HU1QAoAgAiAUEARyAAIAFPcUUEQEHU1QAgADYCAAtBACEBQYjZACAGNgIAQYTZACAANgIAQeTVAEF/NgIAQejVAEGc2QAoAgA2AgBBkNkAQQA2AgADQCABQYDWAGogAUH01QBqIgI2AgAgAiABQezVAGoiAzYCACABQfjVAGogAzYCACABQYjWAGogAUH81QBqIgM2AgAgAyACNgIAIAFBkNYAaiABQYTWAGoiAjYCACACIAM2AgAgAUGM1gBqIAI2AgAgAUEgaiIBQYACRw0AC0F4IABrQQ9xIgEgAGoiAiAGQThrIgMgAWsiAUEBcjYCBEHg1QBBrNkAKAIANgIAQdDVACABNgIAQdzVACACNgIAIAAgA2pBODYCBAwCCyAAIAJNDQAgAiADSQ0AIAEoAgxBCHENAEF4IAJrQQ9xIgAgAmoiA0HQ1QAoAgAgBmoiByAAayIAQQFyNgIEIAEgBSAGajYCBEHg1QBBrNkAKAIANgIAQdDVACAANgIAQdzVACADNgIAIAIgB2pBODYCBAwBCyAAQdTVACgCAEkEQEHU1QAgADYCAAsgACAGaiEDQYTZACEBAkACQAJAA0AgAyABKAIARwRAIAEoAggiAQ0BDAILCyABLQAMQQhxRQ0BC0GE2QAhAQNAIAEoAgAiAyACTQRAIAMgASgCBGoiBSACSw0DCyABKAIIIQEMAAsACyABIAA2AgAgASABKAIEIAZqNgIEIABBeCAAa0EPcWoiCSAEQQNyNgIEIANBeCADa0EPcWoiBiAEIAlqIgRrIQEgAiAGRgRAQdzVACAENgIAQdDVAEHQ1QAoAgAgAWoiADYCACAEIABBAXI2AgQMCAtB2NUAKAIAIAZGBEBB2NUAIAQ2AgBBzNUAQczVACgCACABaiIANgIAIAQgAEEBcjYCBCAAIARqIAA2AgAMCAsgBigCBCIFQQNxQQFHDQYgBUF4cSEIIAVB/wFNBEAgBUEDdiEDIAYoAggiACAGKAIMIgJGBEBBxNUAQcTVACgCAEF+IAN3cTYCAAwHCyACIAA2AgggACACNgIMDAYLIAYoAhghByAGIAYoAgwiAEcEQCAAIAYoAggiAjYCCCACIAA2AgwMBQsgBkEUaiICKAIAIgVFBEAgBigCECIFRQ0EIAZBEGohAgsDQCACIQMgBSIAQRRqIgIoAgAiBQ0AIABBEGohAiAAKAIQIgUNAAsgA0EANgIADAQLQXggAGtBD3EiASAAaiIHIAZBOGsiAyABayIBQQFyNgIEIAAgA2pBODYCBCACIAVBNyAFa0EPcWpBP2siAyADIAJBEGpJGyIDQSM2AgRB4NUAQazZACgCADYCAEHQ1QAgATYCAEHc1QAgBzYCACADQRBqQYzZACkCADcCACADQYTZACkCADcCCEGM2QAgA0EIajYCAEGI2QAgBjYCAEGE2QAgADYCAEGQ2QBBADYCACADQSRqIQEDQCABQQc2AgAgBSABQQRqIgFLDQALIAIgA0YNACADIAMoAgRBfnE2AgQgAyADIAJrIgU2AgAgAiAFQQFyNgIEIAVB/wFNBEAgBUF4cUHs1QBqIQACf0HE1QAoAgAiAUEBIAVBA3Z0IgNxRQRAQcTVACABIANyNgIAIAAMAQsgACgCCAsiASACNgIMIAAgAjYCCCACIAA2AgwgAiABNgIIDAELQR8hASAFQf///wdNBEAgBUEmIAVBCHZnIgBrdkEBcSAAQQF0a0E+aiEBCyACIAE2AhwgAkIANwIQIAFBAnRB9NcAaiEAQcjVACgCACIDQQEgAXQiBnFFBEAgACACNgIAQcjVACADIAZyNgIAIAIgADYCGCACIAI2AgggAiACNgIMDAELIAVBGSABQQF2a0EAIAFBH0cbdCEBIAAoAgAhAwJAA0AgAyIAKAIEQXhxIAVGDQEgAUEddiEDIAFBAXQhASAAIANBBHFqQRBqIgYoAgAiAw0ACyAGIAI2AgAgAiAANgIYIAIgAjYCDCACIAI2AggMAQsgACgCCCIBIAI2AgwgACACNgIIIAJBADYCGCACIAA2AgwgAiABNgIIC0HQ1QAoAgAiASAETQ0AQdzVACgCACIAIARqIgIgASAEayIBQQFyNgIEQdDVACABNgIAQdzVACACNgIAIAAgBEEDcjYCBCAAQQhqIQEMCAtBACEBQbTZAEEwNgIADAcLQQAhAAsgB0UNAAJAIAYoAhwiAkECdEH01wBqIgMoAgAgBkYEQCADIAA2AgAgAA0BQcjVAEHI1QAoAgBBfiACd3E2AgAMAgsgB0EQQRQgBygCECAGRhtqIAA2AgAgAEUNAQsgACAHNgIYIAYoAhAiAgRAIAAgAjYCECACIAA2AhgLIAZBFGooAgAiAkUNACAAQRRqIAI2AgAgAiAANgIYCyABIAhqIQEgBiAIaiIGKAIEIQULIAYgBUF+cTYCBCABIARqIAE2AgAgBCABQQFyNgIEIAFB/wFNBEAgAUF4cUHs1QBqIQACf0HE1QAoAgAiAkEBIAFBA3Z0IgFxRQRAQcTVACABIAJyNgIAIAAMAQsgACgCCAsiASAENgIMIAAgBDYCCCAEIAA2AgwgBCABNgIIDAELQR8hBSABQf///wdNBEAgAUEmIAFBCHZnIgBrdkEBcSAAQQF0a0E+aiEFCyAEIAU2AhwgBEIANwIQIAVBAnRB9NcAaiEAQcjVACgCACICQQEgBXQiA3FFBEAgACAENgIAQcjVACACIANyNgIAIAQgADYCGCAEIAQ2AgggBCAENgIMDAELIAFBGSAFQQF2a0EAIAVBH0cbdCEFIAAoAgAhAAJAA0AgACICKAIEQXhxIAFGDQEgBUEddiEAIAVBAXQhBSACIABBBHFqQRBqIgMoAgAiAA0ACyADIAQ2AgAgBCACNgIYIAQgBDYCDCAEIAQ2AggMAQsgAigCCCIAIAQ2AgwgAiAENgIIIARBADYCGCAEIAI2AgwgBCAANgIICyAJQQhqIQEMAgsCQCAHRQ0AAkAgAygCHCIBQQJ0QfTXAGoiAigCACADRgRAIAIgADYCACAADQFByNUAIAhBfiABd3EiCDYCAAwCCyAHQRBBFCAHKAIQIANGG2ogADYCACAARQ0BCyAAIAc2AhggAygCECIBBEAgACABNgIQIAEgADYCGAsgA0EUaigCACIBRQ0AIABBFGogATYCACABIAA2AhgLAkAgBUEPTQRAIAMgBCAFaiIAQQNyNgIEIAAgA2oiACAAKAIEQQFyNgIEDAELIAMgBGoiAiAFQQFyNgIEIAMgBEEDcjYCBCACIAVqIAU2AgAgBUH/AU0EQCAFQXhxQezVAGohAAJ/QcTVACgCACIBQQEgBUEDdnQiBXFFBEBBxNUAIAEgBXI2AgAgAAwBCyAAKAIICyIBIAI2AgwgACACNgIIIAIgADYCDCACIAE2AggMAQtBHyEBIAVB////B00EQCAFQSYgBUEIdmciAGt2QQFxIABBAXRrQT5qIQELIAIgATYCHCACQgA3AhAgAUECdEH01wBqIQBBASABdCIEIAhxRQRAIAAgAjYCAEHI1QAgBCAIcjYCACACIAA2AhggAiACNgIIIAIgAjYCDAwBCyAFQRkgAUEBdmtBACABQR9HG3QhASAAKAIAIQQCQANAIAQiACgCBEF4cSAFRg0BIAFBHXYhBCABQQF0IQEgACAEQQRxakEQaiIGKAIAIgQNAAsgBiACNgIAIAIgADYCGCACIAI2AgwgAiACNgIIDAELIAAoAggiASACNgIMIAAgAjYCCCACQQA2AhggAiAANgIMIAIgATYCCAsgA0EIaiEBDAELAkAgCUUNAAJAIAAoAhwiAUECdEH01wBqIgIoAgAgAEYEQCACIAM2AgAgAw0BQcjVACALQX4gAXdxNgIADAILIAlBEEEUIAkoAhAgAEYbaiADNgIAIANFDQELIAMgCTYCGCAAKAIQIgEEQCADIAE2AhAgASADNgIYCyAAQRRqKAIAIgFFDQAgA0EUaiABNgIAIAEgAzYCGAsCQCAFQQ9NBEAgACAEIAVqIgFBA3I2AgQgACABaiIBIAEoAgRBAXI2AgQMAQsgACAEaiIHIAVBAXI2AgQgACAEQQNyNgIEIAUgB2ogBTYCACAIBEAgCEF4cUHs1QBqIQFB2NUAKAIAIQMCf0EBIAhBA3Z0IgIgBnFFBEBBxNUAIAIgBnI2AgAgAQwBCyABKAIICyICIAM2AgwgASADNgIIIAMgATYCDCADIAI2AggLQdjVACAHNgIAQczVACAFNgIACyAAQQhqIQELIApBEGokACABC0MAIABFBEA/AEEQdA8LAkAgAEH//wNxDQAgAEEASA0AIABBEHZAACIAQX9GBEBBtNkAQTA2AgBBfw8LIABBEHQPCwALC5lCIgBBgAgLDQEAAAAAAAAAAgAAAAMAQZgICwUEAAAABQBBqAgLCQYAAAAHAAAACABB5AgLwjJJbnZhbGlkIGNoYXIgaW4gdXJsIHF1ZXJ5AFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fYm9keQBDb250ZW50LUxlbmd0aCBvdmVyZmxvdwBDaHVuayBzaXplIG92ZXJmbG93AEludmFsaWQgbWV0aG9kIGZvciBIVFRQL3gueCByZXF1ZXN0AEludmFsaWQgbWV0aG9kIGZvciBSVFNQL3gueCByZXF1ZXN0AEV4cGVjdGVkIFNPVVJDRSBtZXRob2QgZm9yIElDRS94LnggcmVxdWVzdABJbnZhbGlkIGNoYXIgaW4gdXJsIGZyYWdtZW50IHN0YXJ0AEV4cGVjdGVkIGRvdABTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3N0YXR1cwBJbnZhbGlkIHJlc3BvbnNlIHN0YXR1cwBFeHBlY3RlZCBMRiBhZnRlciBoZWFkZXJzAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMAVXNlciBjYWxsYmFjayBlcnJvcgBgb25fcmVzZXRgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19oZWFkZXJgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3Byb3RvY29sX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fcHJvdG9jb2wARW1wdHkgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyYWN0ZXIgaW4gQ29udGVudC1MZW5ndGgAVHJhbnNmZXItRW5jb2RpbmcgY2FuJ3QgYmUgcHJlc2VudCB3aXRoIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgc2l6ZQBFeHBlY3RlZCBMRiBhZnRlciBjaHVuayBzaXplAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBVbmV4cGVjdGVkIHdoaXRlc3BhY2UgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgaGVhZGVyIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgTEYgYWZ0ZXIgaGVhZGVyIHZhbHVlAEludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYCBoZWFkZXIgdmFsdWUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciBjaHVuayBleHRlbnNpb24gdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZSB2YWx1ZQBJbnZhbGlkIHF1b3RlZC1wYWlyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fcHJvdG9jb2xfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUATWlzc2luZyBleHBlY3RlZCBDUiBhZnRlciByZXNwb25zZSBsaW5lAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBuYW1lAE1pc3NpbmcgZXhwZWN0ZWQgQ1IgYWZ0ZXIgY2h1bmsgZXh0ZW5zaW9uIG5hbWUASW52YWxpZCBzdGF0dXMgY29kZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABNaXNzaW5nIGV4cGVjdGVkIENSIGFmdGVyIGNodW5rIGRhdGEARXhwZWN0ZWQgTEYgYWZ0ZXIgY2h1bmsgZGF0YQBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AARGF0YSBhZnRlciBgQ29ubmVjdGlvbjogY2xvc2VgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBRVUVSWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAEV4cGVjdGVkIExGIGFmdGVyIENSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX1BST1RPQ09MX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19DT01QTEVURQBIUEVfQ0JfSEVBREVSX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9OQU1FX0NPTVBMRVRFAEhQRV9DQl9NRVNTQUdFX0NPTVBMRVRFAEhQRV9DQl9NRVRIT0RfQ09NUExFVEUASFBFX0NCX0hFQURFUl9GSUVMRF9DT01QTEVURQBERUxFVEUASFBFX0lOVkFMSURfRU9GX1NUQVRFAElOVkFMSURfU1NMX0NFUlRJRklDQVRFAFBBVVNFAE5PX1JFU1BPTlNFAFVOU1VQUE9SVEVEX01FRElBX1RZUEUAR09ORQBOT1RfQUNDRVBUQUJMRQBTRVJWSUNFX1VOQVZBSUxBQkxFAFJBTkdFX05PVF9TQVRJU0ZJQUJMRQBPUklHSU5fSVNfVU5SRUFDSEFCTEUAUkVTUE9OU0VfSVNfU1RBTEUAUFVSR0UATUVSR0UAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRQBSRVFVRVNUX0hFQURFUl9UT09fTEFSR0UAUEFZTE9BRF9UT09fTEFSR0UASU5TVUZGSUNJRU5UX1NUT1JBR0UASFBFX1BBVVNFRF9VUEdSQURFAEhQRV9QQVVTRURfSDJfVVBHUkFERQBTT1VSQ0UAQU5OT1VOQ0UAVFJBQ0UASFBFX1VORVhQRUNURURfU1BBQ0UAREVTQ1JJQkUAVU5TVUJTQ1JJQkUAUkVDT1JEAEhQRV9JTlZBTElEX01FVEhPRABOT1RfRk9VTkQAUFJPUEZJTkQAVU5CSU5EAFJFQklORABVTkFVVEhPUklaRUQATUVUSE9EX05PVF9BTExPV0VEAEhUVFBfVkVSU0lPTl9OT1RfU1VQUE9SVEVEAEFMUkVBRFlfUkVQT1JURUQAQUNDRVBURUQATk9UX0lNUExFTUVOVEVEAExPT1BfREVURUNURUQASFBFX0NSX0VYUEVDVEVEAEhQRV9MRl9FWFBFQ1RFRABDUkVBVEVEAElNX1VTRUQASFBFX1BBVVNFRABUSU1FT1VUX09DQ1VSRUQAUEFZTUVOVF9SRVFVSVJFRABQUkVDT05ESVRJT05fUkVRVUlSRUQAUFJPWFlfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATkVUV09SS19BVVRIRU5USUNBVElPTl9SRVFVSVJFRABMRU5HVEhfUkVRVUlSRUQAU1NMX0NFUlRJRklDQVRFX1JFUVVJUkVEAFVQR1JBREVfUkVRVUlSRUQAUEFHRV9FWFBJUkVEAFBSRUNPTkRJVElPTl9GQUlMRUQARVhQRUNUQVRJT05fRkFJTEVEAFJFVkFMSURBVElPTl9GQUlMRUQAU1NMX0hBTkRTSEFLRV9GQUlMRUQATE9DS0VEAFRSQU5TRk9STUFUSU9OX0FQUExJRUQATk9UX01PRElGSUVEAE5PVF9FWFRFTkRFRABCQU5EV0lEVEhfTElNSVRfRVhDRUVERUQAU0lURV9JU19PVkVSTE9BREVEAEhFQUQARXhwZWN0ZWQgSFRUUC8sIFJUU1AvIG9yIElDRS8A5xUAAK8VAACkEgAAkhoAACYWAACeFAAA2xkAAHkVAAB+EgAA/hQAADYVAAALFgAA2BYAAPMSAABCGAAArBYAABIVAAAUFwAA7xcAAEgUAABxFwAAshoAAGsZAAB+GQAANRQAAIIaAABEFwAA/RYAAB4YAACHFwAAqhkAAJMSAAAHGAAALBcAAMoXAACkFwAA5xUAAOcVAABYFwAAOxgAAKASAAAtHAAAwxEAAEgRAADeEgAAQhMAAKQZAAD9EAAA9xUAAKUVAADvFgAA+BkAAEoWAABWFgAA9RUAAAoaAAAIGgAAARoAAKsVAABCEgAA1xAAAEwRAAAFGQAAVBYAAB4RAADKGQAAyBkAAE4WAAD/GAAAcRQAAPAVAADuFQAAlBkAAPwVAAC/GQAAmxkAAHwUAABDEQAAcBgAAJUUAAAnFAAAGRQAANUSAADUGQAARBYAAPcQAEG5OwsBAQBB0DsL4AEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBuj0LBAEAAAIAQdE9C14DBAMDAwMDAAADAwADAwADAwMDAwMDAwMDAAUAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAwADAEG6PwsEAQAAAgBB0T8LXgMAAwMDAwMAAAMDAAMDAAMDAwMDAwMDAwMABAAFAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwADAAMAQbDBAAsNbG9zZWVlcC1hbGl2ZQBBycEACwEBAEHgwQAL4AEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBycMACwEBAEHgwwAL5wEBAQEBAQEBAQEBAQECAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAWNodW5rZWQAQfHFAAteAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQBB0McACyFlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AQYDIAAsgcmFuc2Zlci1lbmNvZGluZ3BncmFkZQ0KDQpTTQ0KDQoAQanIAAsFAQIAAQMAQcDIAAtfBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanKAAsFAQIAAQMAQcDKAAtfBAUFBgUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAQanMAAsEAQAAAQBBwcwAC14CAgACAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAEGpzgALBQECAAEDAEHAzgALXwQFAAAFBQUFBQUFBQUFBQYFBQUFBQUFBQUFBQUABQAHCAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQAFAAUABQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUAAAAFAEGp0AALBQEBAAEBAEHA0AALAQEAQdrQAAtBAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQanSAAsFAQEAAQEAQcDSAAsBAQBBytIACwYCAAAAAAIAQeHSAAs6AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBoNQAC50BTk9VTkNFRUNLT1VUTkVDVEVURUNSSUJFTFVTSEVURUFEU0VBUkNIUkdFQ1RJVklUWUxFTkRBUlZFT1RJRllQVElPTlNDSFNFQVlTVEFUQ0hHRVVFUllPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFVFRQQ0VUU1BBRFRQLw=='\n\nlet wasmBuffer\n\nObject.defineProperty(module, 'exports', {\n  get: () => {\n    return wasmBuffer\n      ? wasmBuffer\n      : (wasmBuffer = Buffer.from(wasmBase64, 'base64'))\n  }\n})\n"
  },
  {
    "path": "lib/llhttp/utils.d.ts",
    "content": "import type { IntDict } from './constants';\nexport declare function enumToMap(obj: IntDict, filter?: readonly number[], exceptions?: readonly number[]): IntDict;\n"
  },
  {
    "path": "lib/llhttp/utils.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.enumToMap = enumToMap;\nfunction enumToMap(obj, filter = [], exceptions = []) {\n    const emptyFilter = (filter?.length ?? 0) === 0;\n    const emptyExceptions = (exceptions?.length ?? 0) === 0;\n    return Object.fromEntries(Object.entries(obj).filter(([, value]) => {\n        return (typeof value === 'number' &&\n            (emptyFilter || filter.includes(value)) &&\n            (emptyExceptions || !exceptions.includes(value)));\n    }));\n}\n"
  },
  {
    "path": "lib/mock/mock-agent.js",
    "content": "'use strict'\n\nconst { kClients } = require('../core/symbols')\nconst Agent = require('../dispatcher/agent')\nconst {\n  kAgent,\n  kMockAgentSet,\n  kMockAgentGet,\n  kDispatches,\n  kIsMockActive,\n  kNetConnect,\n  kGetNetConnect,\n  kOptions,\n  kFactory,\n  kMockAgentRegisterCallHistory,\n  kMockAgentIsCallHistoryEnabled,\n  kMockAgentAddCallHistoryLog,\n  kMockAgentMockCallHistoryInstance,\n  kMockAgentAcceptsNonStandardSearchParameters,\n  kMockCallHistoryAddLog,\n  kIgnoreTrailingSlash\n} = require('./mock-symbols')\nconst MockClient = require('./mock-client')\nconst MockPool = require('./mock-pool')\nconst { matchValue, normalizeSearchParams, buildAndValidateMockOptions, normalizeOrigin } = require('./mock-utils')\nconst { InvalidArgumentError, UndiciError } = require('../core/errors')\nconst Dispatcher = require('../dispatcher/dispatcher')\nconst PendingInterceptorsFormatter = require('./pending-interceptors-formatter')\nconst { MockCallHistory } = require('./mock-call-history')\n\nclass MockAgent extends Dispatcher {\n  constructor (opts = {}) {\n    super(opts)\n\n    const mockOptions = buildAndValidateMockOptions(opts)\n\n    this[kNetConnect] = true\n    this[kIsMockActive] = true\n    this[kMockAgentIsCallHistoryEnabled] = mockOptions.enableCallHistory ?? false\n    this[kMockAgentAcceptsNonStandardSearchParameters] = mockOptions.acceptNonStandardSearchParameters ?? false\n    this[kIgnoreTrailingSlash] = mockOptions.ignoreTrailingSlash ?? false\n\n    // Instantiate Agent and encapsulate\n    if (opts?.agent && typeof opts.agent.dispatch !== 'function') {\n      throw new InvalidArgumentError('Argument opts.agent must implement Agent')\n    }\n    const agent = opts?.agent ? opts.agent : new Agent(opts)\n    this[kAgent] = agent\n\n    this[kClients] = agent[kClients]\n    this[kOptions] = mockOptions\n\n    if (this[kMockAgentIsCallHistoryEnabled]) {\n      this[kMockAgentRegisterCallHistory]()\n    }\n  }\n\n  get (origin) {\n    // Normalize origin to handle URL objects and case-insensitive hostnames\n    const normalizedOrigin = normalizeOrigin(origin)\n    const originKey = this[kIgnoreTrailingSlash] ? normalizedOrigin.replace(/\\/$/, '') : normalizedOrigin\n\n    let dispatcher = this[kMockAgentGet](originKey)\n\n    if (!dispatcher) {\n      dispatcher = this[kFactory](originKey)\n      this[kMockAgentSet](originKey, dispatcher)\n    }\n    return dispatcher\n  }\n\n  dispatch (opts, handler) {\n    opts.origin = normalizeOrigin(opts.origin)\n\n    // Call MockAgent.get to perform additional setup before dispatching as normal\n    this.get(opts.origin)\n\n    this[kMockAgentAddCallHistoryLog](opts)\n\n    const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters]\n\n    const dispatchOpts = { ...opts }\n\n    if (acceptNonStandardSearchParameters && dispatchOpts.path) {\n      const [path, searchParams] = dispatchOpts.path.split('?')\n      const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters)\n      dispatchOpts.path = `${path}?${normalizedSearchParams}`\n    }\n\n    return this[kAgent].dispatch(dispatchOpts, handler)\n  }\n\n  async close () {\n    this.clearCallHistory()\n    await this[kAgent].close()\n    this[kClients].clear()\n  }\n\n  deactivate () {\n    this[kIsMockActive] = false\n  }\n\n  activate () {\n    this[kIsMockActive] = true\n  }\n\n  enableNetConnect (matcher) {\n    if (typeof matcher === 'string' || typeof matcher === 'function' || matcher instanceof RegExp) {\n      if (Array.isArray(this[kNetConnect])) {\n        this[kNetConnect].push(matcher)\n      } else {\n        this[kNetConnect] = [matcher]\n      }\n    } else if (typeof matcher === 'undefined') {\n      this[kNetConnect] = true\n    } else {\n      throw new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.')\n    }\n  }\n\n  disableNetConnect () {\n    this[kNetConnect] = false\n  }\n\n  enableCallHistory () {\n    this[kMockAgentIsCallHistoryEnabled] = true\n\n    return this\n  }\n\n  disableCallHistory () {\n    this[kMockAgentIsCallHistoryEnabled] = false\n\n    return this\n  }\n\n  getCallHistory () {\n    return this[kMockAgentMockCallHistoryInstance]\n  }\n\n  clearCallHistory () {\n    if (this[kMockAgentMockCallHistoryInstance] !== undefined) {\n      this[kMockAgentMockCallHistoryInstance].clear()\n    }\n  }\n\n  // This is required to bypass issues caused by using global symbols - see:\n  // https://github.com/nodejs/undici/issues/1447\n  get isMockActive () {\n    return this[kIsMockActive]\n  }\n\n  [kMockAgentRegisterCallHistory] () {\n    if (this[kMockAgentMockCallHistoryInstance] === undefined) {\n      this[kMockAgentMockCallHistoryInstance] = new MockCallHistory()\n    }\n  }\n\n  [kMockAgentAddCallHistoryLog] (opts) {\n    if (this[kMockAgentIsCallHistoryEnabled]) {\n      // additional setup when enableCallHistory class method is used after mockAgent instantiation\n      this[kMockAgentRegisterCallHistory]()\n\n      // add call history log on every call (intercepted or not)\n      this[kMockAgentMockCallHistoryInstance][kMockCallHistoryAddLog](opts)\n    }\n  }\n\n  [kMockAgentSet] (origin, dispatcher) {\n    this[kClients].set(origin, { count: 0, dispatcher })\n  }\n\n  [kFactory] (origin) {\n    const mockOptions = Object.assign({ agent: this }, this[kOptions])\n    return this[kOptions] && this[kOptions].connections === 1\n      ? new MockClient(origin, mockOptions)\n      : new MockPool(origin, mockOptions)\n  }\n\n  [kMockAgentGet] (origin) {\n    // First check if we can immediately find it\n    const result = this[kClients].get(origin)\n    if (result?.dispatcher) {\n      return result.dispatcher\n    }\n\n    // If the origin is not a string create a dummy parent pool and return to user\n    if (typeof origin !== 'string') {\n      const dispatcher = this[kFactory]('http://localhost:9999')\n      this[kMockAgentSet](origin, dispatcher)\n      return dispatcher\n    }\n\n    // If we match, create a pool and assign the same dispatches\n    for (const [keyMatcher, result] of Array.from(this[kClients])) {\n      if (result && typeof keyMatcher !== 'string' && matchValue(keyMatcher, origin)) {\n        const dispatcher = this[kFactory](origin)\n        this[kMockAgentSet](origin, dispatcher)\n        dispatcher[kDispatches] = result.dispatcher[kDispatches]\n        return dispatcher\n      }\n    }\n  }\n\n  [kGetNetConnect] () {\n    return this[kNetConnect]\n  }\n\n  pendingInterceptors () {\n    const mockAgentClients = this[kClients]\n\n    return Array.from(mockAgentClients.entries())\n      .flatMap(([origin, result]) => result.dispatcher[kDispatches].map(dispatch => ({ ...dispatch, origin })))\n      .filter(({ pending }) => pending)\n  }\n\n  assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) {\n    const pending = this.pendingInterceptors()\n\n    if (pending.length === 0) {\n      return\n    }\n\n    throw new UndiciError(\n      pending.length === 1\n        ? `1 interceptor is pending:\\n\\n${pendingInterceptorsFormatter.format(pending)}`.trim()\n        : `${pending.length} interceptors are pending:\\n\\n${pendingInterceptorsFormatter.format(pending)}`.trim()\n    )\n  }\n}\n\nmodule.exports = MockAgent\n"
  },
  {
    "path": "lib/mock/mock-call-history.js",
    "content": "'use strict'\n\nconst { kMockCallHistoryAddLog } = require('./mock-symbols')\nconst { InvalidArgumentError } = require('../core/errors')\n\nfunction handleFilterCallsWithOptions (criteria, options, handler, store) {\n  switch (options.operator) {\n    case 'OR':\n      store.push(...handler(criteria))\n\n      return store\n    case 'AND':\n      return handler.call({ logs: store }, criteria)\n    default:\n      // guard -- should never happens because buildAndValidateFilterCallsOptions is called before\n      throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \\'OR\\' or \\'AND\\'')\n  }\n}\n\nfunction buildAndValidateFilterCallsOptions (options = {}) {\n  const finalOptions = {}\n\n  if ('operator' in options) {\n    if (typeof options.operator !== 'string' || (options.operator.toUpperCase() !== 'OR' && options.operator.toUpperCase() !== 'AND')) {\n      throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \\'OR\\' or \\'AND\\'')\n    }\n\n    return {\n      ...finalOptions,\n      operator: options.operator.toUpperCase()\n    }\n  }\n\n  return finalOptions\n}\n\nfunction makeFilterCalls (parameterName) {\n  return (parameterValue) => {\n    if (typeof parameterValue === 'string' || parameterValue == null) {\n      return this.logs.filter((log) => {\n        return log[parameterName] === parameterValue\n      })\n    }\n    if (parameterValue instanceof RegExp) {\n      return this.logs.filter((log) => {\n        return parameterValue.test(log[parameterName])\n      })\n    }\n\n    throw new InvalidArgumentError(`${parameterName} parameter should be one of string, regexp, undefined or null`)\n  }\n}\nfunction computeUrlWithMaybeSearchParameters (requestInit) {\n  // path can contains query url parameters\n  // or query can contains query url parameters\n  try {\n    const url = new URL(requestInit.path, requestInit.origin)\n\n    // requestInit.path contains query url parameters\n    // requestInit.query is then undefined\n    if (url.search.length !== 0) {\n      return url\n    }\n\n    // requestInit.query can be populated here\n    url.search = new URLSearchParams(requestInit.query).toString()\n\n    return url\n  } catch (error) {\n    throw new InvalidArgumentError('An error occurred when computing MockCallHistoryLog.url', { cause: error })\n  }\n}\n\nclass MockCallHistoryLog {\n  constructor (requestInit = {}) {\n    this.body = requestInit.body\n    this.headers = requestInit.headers\n    this.method = requestInit.method\n\n    const url = computeUrlWithMaybeSearchParameters(requestInit)\n\n    this.fullUrl = url.toString()\n    this.origin = url.origin\n    this.path = url.pathname\n    this.searchParams = Object.fromEntries(url.searchParams)\n    this.protocol = url.protocol\n    this.host = url.host\n    this.port = url.port\n    this.hash = url.hash\n  }\n\n  toMap () {\n    return new Map([\n      ['protocol', this.protocol],\n      ['host', this.host],\n      ['port', this.port],\n      ['origin', this.origin],\n      ['path', this.path],\n      ['hash', this.hash],\n      ['searchParams', this.searchParams],\n      ['fullUrl', this.fullUrl],\n      ['method', this.method],\n      ['body', this.body],\n      ['headers', this.headers]]\n    )\n  }\n\n  toString () {\n    const options = { betweenKeyValueSeparator: '->', betweenPairSeparator: '|' }\n    let result = ''\n\n    this.toMap().forEach((value, key) => {\n      if (typeof value === 'string' || value === undefined || value === null) {\n        result = `${result}${key}${options.betweenKeyValueSeparator}${value}${options.betweenPairSeparator}`\n      }\n      if ((typeof value === 'object' && value !== null) || Array.isArray(value)) {\n        result = `${result}${key}${options.betweenKeyValueSeparator}${JSON.stringify(value)}${options.betweenPairSeparator}`\n      }\n      // maybe miss something for non Record / Array headers and searchParams here\n    })\n\n    // delete last betweenPairSeparator\n    return result.slice(0, -1)\n  }\n}\n\nclass MockCallHistory {\n  logs = []\n\n  calls () {\n    return this.logs\n  }\n\n  firstCall () {\n    return this.logs.at(0)\n  }\n\n  lastCall () {\n    return this.logs.at(-1)\n  }\n\n  nthCall (number) {\n    if (typeof number !== 'number') {\n      throw new InvalidArgumentError('nthCall must be called with a number')\n    }\n    if (!Number.isInteger(number)) {\n      throw new InvalidArgumentError('nthCall must be called with an integer')\n    }\n    if (Math.sign(number) !== 1) {\n      throw new InvalidArgumentError('nthCall must be called with a positive value. use firstCall or lastCall instead')\n    }\n\n    // non zero based index. this is more human readable\n    return this.logs.at(number - 1)\n  }\n\n  filterCalls (criteria, options) {\n    // perf\n    if (this.logs.length === 0) {\n      return this.logs\n    }\n    if (typeof criteria === 'function') {\n      return this.logs.filter(criteria)\n    }\n    if (criteria instanceof RegExp) {\n      return this.logs.filter((log) => {\n        return criteria.test(log.toString())\n      })\n    }\n    if (typeof criteria === 'object' && criteria !== null) {\n      // no criteria - returning all logs\n      if (Object.keys(criteria).length === 0) {\n        return this.logs\n      }\n\n      const finalOptions = { operator: 'OR', ...buildAndValidateFilterCallsOptions(options) }\n\n      let maybeDuplicatedLogsFiltered = []\n      if ('protocol' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.protocol, finalOptions, this.filterCallsByProtocol, maybeDuplicatedLogsFiltered)\n      }\n      if ('host' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.host, finalOptions, this.filterCallsByHost, maybeDuplicatedLogsFiltered)\n      }\n      if ('port' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.port, finalOptions, this.filterCallsByPort, maybeDuplicatedLogsFiltered)\n      }\n      if ('origin' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.origin, finalOptions, this.filterCallsByOrigin, maybeDuplicatedLogsFiltered)\n      }\n      if ('path' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.path, finalOptions, this.filterCallsByPath, maybeDuplicatedLogsFiltered)\n      }\n      if ('hash' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.hash, finalOptions, this.filterCallsByHash, maybeDuplicatedLogsFiltered)\n      }\n      if ('fullUrl' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.fullUrl, finalOptions, this.filterCallsByFullUrl, maybeDuplicatedLogsFiltered)\n      }\n      if ('method' in criteria) {\n        maybeDuplicatedLogsFiltered = handleFilterCallsWithOptions(criteria.method, finalOptions, this.filterCallsByMethod, maybeDuplicatedLogsFiltered)\n      }\n\n      const uniqLogsFiltered = [...new Set(maybeDuplicatedLogsFiltered)]\n\n      return uniqLogsFiltered\n    }\n\n    throw new InvalidArgumentError('criteria parameter should be one of function, regexp, or object')\n  }\n\n  filterCallsByProtocol = makeFilterCalls.call(this, 'protocol')\n\n  filterCallsByHost = makeFilterCalls.call(this, 'host')\n\n  filterCallsByPort = makeFilterCalls.call(this, 'port')\n\n  filterCallsByOrigin = makeFilterCalls.call(this, 'origin')\n\n  filterCallsByPath = makeFilterCalls.call(this, 'path')\n\n  filterCallsByHash = makeFilterCalls.call(this, 'hash')\n\n  filterCallsByFullUrl = makeFilterCalls.call(this, 'fullUrl')\n\n  filterCallsByMethod = makeFilterCalls.call(this, 'method')\n\n  clear () {\n    this.logs = []\n  }\n\n  [kMockCallHistoryAddLog] (requestInit) {\n    const log = new MockCallHistoryLog(requestInit)\n\n    this.logs.push(log)\n\n    return log\n  }\n\n  * [Symbol.iterator] () {\n    for (const log of this.calls()) {\n      yield log\n    }\n  }\n}\n\nmodule.exports.MockCallHistory = MockCallHistory\nmodule.exports.MockCallHistoryLog = MockCallHistoryLog\n"
  },
  {
    "path": "lib/mock/mock-client.js",
    "content": "'use strict'\n\nconst { promisify } = require('node:util')\nconst Client = require('../dispatcher/client')\nconst { buildMockDispatch } = require('./mock-utils')\nconst {\n  kDispatches,\n  kMockAgent,\n  kClose,\n  kOriginalClose,\n  kOrigin,\n  kOriginalDispatch,\n  kConnected,\n  kIgnoreTrailingSlash\n} = require('./mock-symbols')\nconst { MockInterceptor } = require('./mock-interceptor')\nconst Symbols = require('../core/symbols')\nconst { InvalidArgumentError } = require('../core/errors')\n\n/**\n * MockClient provides an API that extends the Client to influence the mockDispatches.\n */\nclass MockClient extends Client {\n  constructor (origin, opts) {\n    if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {\n      throw new InvalidArgumentError('Argument opts.agent must implement Agent')\n    }\n\n    super(origin, opts)\n\n    this[kMockAgent] = opts.agent\n    this[kOrigin] = origin\n    this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false\n    this[kDispatches] = []\n    this[kConnected] = 1\n    this[kOriginalDispatch] = this.dispatch\n    this[kOriginalClose] = this.close.bind(this)\n\n    this.dispatch = buildMockDispatch.call(this)\n    this.close = this[kClose]\n  }\n\n  get [Symbols.kConnected] () {\n    return this[kConnected]\n  }\n\n  /**\n   * Sets up the base interceptor for mocking replies from undici.\n   */\n  intercept (opts) {\n    return new MockInterceptor(\n      opts && { ignoreTrailingSlash: this[kIgnoreTrailingSlash], ...opts },\n      this[kDispatches]\n    )\n  }\n\n  cleanMocks () {\n    this[kDispatches] = []\n  }\n\n  async [kClose] () {\n    await promisify(this[kOriginalClose])()\n    this[kConnected] = 0\n    this[kMockAgent][Symbols.kClients].delete(this[kOrigin])\n  }\n}\n\nmodule.exports = MockClient\n"
  },
  {
    "path": "lib/mock/mock-errors.js",
    "content": "'use strict'\n\nconst { UndiciError } = require('../core/errors')\n\nconst kMockNotMatchedError = Symbol.for('undici.error.UND_MOCK_ERR_MOCK_NOT_MATCHED')\n\n/**\n * The request does not match any registered mock dispatches.\n */\nclass MockNotMatchedError extends UndiciError {\n  constructor (message) {\n    super(message)\n    this.name = 'MockNotMatchedError'\n    this.message = message || 'The request does not match any registered mock dispatches'\n    this.code = 'UND_MOCK_ERR_MOCK_NOT_MATCHED'\n  }\n\n  static [Symbol.hasInstance] (instance) {\n    return instance && instance[kMockNotMatchedError] === true\n  }\n\n  get [kMockNotMatchedError] () {\n    return true\n  }\n}\n\nmodule.exports = {\n  MockNotMatchedError\n}\n"
  },
  {
    "path": "lib/mock/mock-interceptor.js",
    "content": "'use strict'\n\nconst { getResponseData, buildKey, addMockDispatch } = require('./mock-utils')\nconst {\n  kDispatches,\n  kDispatchKey,\n  kDefaultHeaders,\n  kDefaultTrailers,\n  kContentLength,\n  kMockDispatch,\n  kIgnoreTrailingSlash\n} = require('./mock-symbols')\nconst { InvalidArgumentError } = require('../core/errors')\nconst { serializePathWithQuery } = require('../core/util')\n\n/**\n * Defines the scope API for an interceptor reply\n */\nclass MockScope {\n  constructor (mockDispatch) {\n    this[kMockDispatch] = mockDispatch\n  }\n\n  /**\n   * Delay a reply by a set amount in ms.\n   */\n  delay (waitInMs) {\n    if (typeof waitInMs !== 'number' || !Number.isInteger(waitInMs) || waitInMs <= 0) {\n      throw new InvalidArgumentError('waitInMs must be a valid integer > 0')\n    }\n\n    this[kMockDispatch].delay = waitInMs\n    return this\n  }\n\n  /**\n   * For a defined reply, never mark as consumed.\n   */\n  persist () {\n    this[kMockDispatch].persist = true\n    return this\n  }\n\n  /**\n   * Allow one to define a reply for a set amount of matching requests.\n   */\n  times (repeatTimes) {\n    if (typeof repeatTimes !== 'number' || !Number.isInteger(repeatTimes) || repeatTimes <= 0) {\n      throw new InvalidArgumentError('repeatTimes must be a valid integer > 0')\n    }\n\n    this[kMockDispatch].times = repeatTimes\n    return this\n  }\n}\n\n/**\n * Defines an interceptor for a Mock\n */\nclass MockInterceptor {\n  constructor (opts, mockDispatches) {\n    if (typeof opts !== 'object') {\n      throw new InvalidArgumentError('opts must be an object')\n    }\n    if (typeof opts.path === 'undefined') {\n      throw new InvalidArgumentError('opts.path must be defined')\n    }\n    if (typeof opts.method === 'undefined') {\n      opts.method = 'GET'\n    }\n    // See https://github.com/nodejs/undici/issues/1245\n    // As per RFC 3986, clients are not supposed to send URI\n    // fragments to servers when they retrieve a document,\n    if (typeof opts.path === 'string') {\n      if (opts.query) {\n        opts.path = serializePathWithQuery(opts.path, opts.query)\n      } else {\n        // Matches https://github.com/nodejs/undici/blob/main/lib/web/fetch/index.js#L1811\n        const parsedURL = new URL(opts.path, 'data://')\n        opts.path = parsedURL.pathname + parsedURL.search\n      }\n    }\n    if (typeof opts.method === 'string') {\n      opts.method = opts.method.toUpperCase()\n    }\n\n    this[kDispatchKey] = buildKey(opts)\n    this[kDispatches] = mockDispatches\n    this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false\n    this[kDefaultHeaders] = {}\n    this[kDefaultTrailers] = {}\n    this[kContentLength] = false\n  }\n\n  createMockScopeDispatchData ({ statusCode, data, responseOptions }) {\n    const responseData = getResponseData(data)\n    const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {}\n    const headers = { ...this[kDefaultHeaders], ...contentLength, ...responseOptions.headers }\n    const trailers = { ...this[kDefaultTrailers], ...responseOptions.trailers }\n\n    return { statusCode, data, headers, trailers }\n  }\n\n  validateReplyParameters (replyParameters) {\n    if (typeof replyParameters.statusCode === 'undefined') {\n      throw new InvalidArgumentError('statusCode must be defined')\n    }\n    if (typeof replyParameters.responseOptions !== 'object' || replyParameters.responseOptions === null) {\n      throw new InvalidArgumentError('responseOptions must be an object')\n    }\n  }\n\n  /**\n   * Mock an undici request with a defined reply.\n   */\n  reply (replyOptionsCallbackOrStatusCode) {\n    // Values of reply aren't available right now as they\n    // can only be available when the reply callback is invoked.\n    if (typeof replyOptionsCallbackOrStatusCode === 'function') {\n      // We'll first wrap the provided callback in another function,\n      // this function will properly resolve the data from the callback\n      // when invoked.\n      const wrappedDefaultsCallback = (opts) => {\n        // Our reply options callback contains the parameter for statusCode, data and options.\n        const resolvedData = replyOptionsCallbackOrStatusCode(opts)\n\n        // Check if it is in the right format\n        if (typeof resolvedData !== 'object' || resolvedData === null) {\n          throw new InvalidArgumentError('reply options callback must return an object')\n        }\n\n        const replyParameters = { data: '', responseOptions: {}, ...resolvedData }\n        this.validateReplyParameters(replyParameters)\n        // Since the values can be obtained immediately we return them\n        // from this higher order function that will be resolved later.\n        return {\n          ...this.createMockScopeDispatchData(replyParameters)\n        }\n      }\n\n      // Add usual dispatch data, but this time set the data parameter to function that will eventually provide data.\n      const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], wrappedDefaultsCallback, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })\n      return new MockScope(newMockDispatch)\n    }\n\n    // We can have either one or three parameters, if we get here,\n    // we should have 1-3 parameters. So we spread the arguments of\n    // this function to obtain the parameters, since replyData will always\n    // just be the statusCode.\n    const replyParameters = {\n      statusCode: replyOptionsCallbackOrStatusCode,\n      data: arguments[1] === undefined ? '' : arguments[1],\n      responseOptions: arguments[2] === undefined ? {} : arguments[2]\n    }\n    this.validateReplyParameters(replyParameters)\n\n    // Send in-already provided data like usual\n    const dispatchData = this.createMockScopeDispatchData(replyParameters)\n    const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })\n    return new MockScope(newMockDispatch)\n  }\n\n  /**\n   * Mock an undici request with a defined error.\n   */\n  replyWithError (error) {\n    if (typeof error === 'undefined') {\n      throw new InvalidArgumentError('error must be defined')\n    }\n\n    const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], { error }, { ignoreTrailingSlash: this[kIgnoreTrailingSlash] })\n    return new MockScope(newMockDispatch)\n  }\n\n  /**\n   * Set default reply headers on the interceptor for subsequent replies\n   */\n  defaultReplyHeaders (headers) {\n    if (typeof headers === 'undefined') {\n      throw new InvalidArgumentError('headers must be defined')\n    }\n\n    this[kDefaultHeaders] = headers\n    return this\n  }\n\n  /**\n   * Set default reply trailers on the interceptor for subsequent replies\n   */\n  defaultReplyTrailers (trailers) {\n    if (typeof trailers === 'undefined') {\n      throw new InvalidArgumentError('trailers must be defined')\n    }\n\n    this[kDefaultTrailers] = trailers\n    return this\n  }\n\n  /**\n   * Set reply content length header for replies on the interceptor\n   */\n  replyContentLength () {\n    this[kContentLength] = true\n    return this\n  }\n}\n\nmodule.exports.MockInterceptor = MockInterceptor\nmodule.exports.MockScope = MockScope\n"
  },
  {
    "path": "lib/mock/mock-pool.js",
    "content": "'use strict'\n\nconst { promisify } = require('node:util')\nconst Pool = require('../dispatcher/pool')\nconst { buildMockDispatch } = require('./mock-utils')\nconst {\n  kDispatches,\n  kMockAgent,\n  kClose,\n  kOriginalClose,\n  kOrigin,\n  kOriginalDispatch,\n  kConnected,\n  kIgnoreTrailingSlash\n} = require('./mock-symbols')\nconst { MockInterceptor } = require('./mock-interceptor')\nconst Symbols = require('../core/symbols')\nconst { InvalidArgumentError } = require('../core/errors')\n\n/**\n * MockPool provides an API that extends the Pool to influence the mockDispatches.\n */\nclass MockPool extends Pool {\n  constructor (origin, opts) {\n    if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') {\n      throw new InvalidArgumentError('Argument opts.agent must implement Agent')\n    }\n\n    super(origin, opts)\n\n    this[kMockAgent] = opts.agent\n    this[kOrigin] = origin\n    this[kIgnoreTrailingSlash] = opts.ignoreTrailingSlash ?? false\n    this[kDispatches] = []\n    this[kConnected] = 1\n    this[kOriginalDispatch] = this.dispatch\n    this[kOriginalClose] = this.close.bind(this)\n\n    this.dispatch = buildMockDispatch.call(this)\n    this.close = this[kClose]\n  }\n\n  get [Symbols.kConnected] () {\n    return this[kConnected]\n  }\n\n  /**\n   * Sets up the base interceptor for mocking replies from undici.\n   */\n  intercept (opts) {\n    return new MockInterceptor(\n      opts && { ignoreTrailingSlash: this[kIgnoreTrailingSlash], ...opts },\n      this[kDispatches]\n    )\n  }\n\n  cleanMocks () {\n    this[kDispatches] = []\n  }\n\n  async [kClose] () {\n    await promisify(this[kOriginalClose])()\n    this[kConnected] = 0\n    this[kMockAgent][Symbols.kClients].delete(this[kOrigin])\n  }\n}\n\nmodule.exports = MockPool\n"
  },
  {
    "path": "lib/mock/mock-symbols.js",
    "content": "'use strict'\n\nmodule.exports = {\n  kAgent: Symbol('agent'),\n  kOptions: Symbol('options'),\n  kFactory: Symbol('factory'),\n  kDispatches: Symbol('dispatches'),\n  kDispatchKey: Symbol('dispatch key'),\n  kDefaultHeaders: Symbol('default headers'),\n  kDefaultTrailers: Symbol('default trailers'),\n  kContentLength: Symbol('content length'),\n  kMockAgent: Symbol('mock agent'),\n  kMockAgentSet: Symbol('mock agent set'),\n  kMockAgentGet: Symbol('mock agent get'),\n  kMockDispatch: Symbol('mock dispatch'),\n  kClose: Symbol('close'),\n  kOriginalClose: Symbol('original agent close'),\n  kOriginalDispatch: Symbol('original dispatch'),\n  kOrigin: Symbol('origin'),\n  kIsMockActive: Symbol('is mock active'),\n  kNetConnect: Symbol('net connect'),\n  kGetNetConnect: Symbol('get net connect'),\n  kConnected: Symbol('connected'),\n  kIgnoreTrailingSlash: Symbol('ignore trailing slash'),\n  kMockAgentMockCallHistoryInstance: Symbol('mock agent mock call history name'),\n  kMockAgentRegisterCallHistory: Symbol('mock agent register mock call history'),\n  kMockAgentAddCallHistoryLog: Symbol('mock agent add call history log'),\n  kMockAgentIsCallHistoryEnabled: Symbol('mock agent is call history enabled'),\n  kMockAgentAcceptsNonStandardSearchParameters: Symbol('mock agent accepts non standard search parameters'),\n  kMockCallHistoryAddLog: Symbol('mock call history add log')\n}\n"
  },
  {
    "path": "lib/mock/mock-utils.js",
    "content": "'use strict'\n\nconst { MockNotMatchedError } = require('./mock-errors')\nconst {\n  kDispatches,\n  kMockAgent,\n  kOriginalDispatch,\n  kOrigin,\n  kGetNetConnect\n} = require('./mock-symbols')\nconst { serializePathWithQuery } = require('../core/util')\nconst { STATUS_CODES } = require('node:http')\nconst {\n  types: {\n    isPromise\n  }\n} = require('node:util')\nconst { InvalidArgumentError } = require('../core/errors')\n\nfunction matchValue (match, value) {\n  if (typeof match === 'string') {\n    return match === value\n  }\n  if (match instanceof RegExp) {\n    return match.test(value)\n  }\n  if (typeof match === 'function') {\n    return match(value) === true\n  }\n  return false\n}\n\nfunction lowerCaseEntries (headers) {\n  return Object.fromEntries(\n    Object.entries(headers).map(([headerName, headerValue]) => {\n      return [headerName.toLocaleLowerCase(), headerValue]\n    })\n  )\n}\n\n/**\n * @param {import('../../index').Headers|string[]|Record<string, string>} headers\n * @param {string} key\n */\nfunction getHeaderByName (headers, key) {\n  if (Array.isArray(headers)) {\n    for (let i = 0; i < headers.length; i += 2) {\n      if (headers[i].toLocaleLowerCase() === key.toLocaleLowerCase()) {\n        return headers[i + 1]\n      }\n    }\n\n    return undefined\n  } else if (typeof headers.get === 'function') {\n    return headers.get(key)\n  } else {\n    return lowerCaseEntries(headers)[key.toLocaleLowerCase()]\n  }\n}\n\n/** @param {string[]} headers */\nfunction buildHeadersFromArray (headers) { // fetch HeadersList\n  const clone = headers.slice()\n  const entries = []\n  for (let index = 0; index < clone.length; index += 2) {\n    entries.push([clone[index], clone[index + 1]])\n  }\n  return Object.fromEntries(entries)\n}\n\nfunction matchHeaders (mockDispatch, headers) {\n  if (typeof mockDispatch.headers === 'function') {\n    if (Array.isArray(headers)) { // fetch HeadersList\n      headers = buildHeadersFromArray(headers)\n    }\n    return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {})\n  }\n  if (typeof mockDispatch.headers === 'undefined') {\n    return true\n  }\n  if (typeof headers !== 'object' || typeof mockDispatch.headers !== 'object') {\n    return false\n  }\n\n  for (const [matchHeaderName, matchHeaderValue] of Object.entries(mockDispatch.headers)) {\n    const headerValue = getHeaderByName(headers, matchHeaderName)\n\n    if (!matchValue(matchHeaderValue, headerValue)) {\n      return false\n    }\n  }\n  return true\n}\n\nfunction normalizeSearchParams (query) {\n  if (typeof query !== 'string') {\n    return query\n  }\n\n  const originalQp = new URLSearchParams(query)\n  const normalizedQp = new URLSearchParams()\n\n  for (let [key, value] of originalQp.entries()) {\n    key = key.replace('[]', '')\n\n    const valueRepresentsString = /^(['\"]).*\\1$/.test(value)\n    if (valueRepresentsString) {\n      normalizedQp.append(key, value)\n      continue\n    }\n\n    if (value.includes(',')) {\n      const values = value.split(',')\n      for (const v of values) {\n        normalizedQp.append(key, v)\n      }\n      continue\n    }\n\n    normalizedQp.append(key, value)\n  }\n\n  return normalizedQp\n}\n\nfunction safeUrl (path) {\n  if (typeof path !== 'string') {\n    return path\n  }\n  const pathSegments = path.split('?', 3)\n  if (pathSegments.length !== 2) {\n    return path\n  }\n\n  const qp = new URLSearchParams(pathSegments.pop())\n  qp.sort()\n  return [...pathSegments, qp.toString()].join('?')\n}\n\nfunction matchKey (mockDispatch, { path, method, body, headers }) {\n  const pathMatch = matchValue(mockDispatch.path, path)\n  const methodMatch = matchValue(mockDispatch.method, method)\n  const bodyMatch = typeof mockDispatch.body !== 'undefined' ? matchValue(mockDispatch.body, body) : true\n  const headersMatch = matchHeaders(mockDispatch, headers)\n  return pathMatch && methodMatch && bodyMatch && headersMatch\n}\n\nfunction getResponseData (data) {\n  if (Buffer.isBuffer(data)) {\n    return data\n  } else if (data instanceof Uint8Array) {\n    return data\n  } else if (data instanceof ArrayBuffer) {\n    return data\n  } else if (typeof data === 'object') {\n    return JSON.stringify(data)\n  } else if (data) {\n    return data.toString()\n  } else {\n    return ''\n  }\n}\n\nfunction getMockDispatch (mockDispatches, key) {\n  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path\n  const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath\n\n  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath)\n\n  // Match path\n  let matchedMockDispatches = mockDispatches\n    .filter(({ consumed }) => !consumed)\n    .filter(({ path, ignoreTrailingSlash }) => {\n      return ignoreTrailingSlash\n        ? matchValue(removeTrailingSlash(safeUrl(path)), resolvedPathWithoutTrailingSlash)\n        : matchValue(safeUrl(path), resolvedPath)\n    })\n  if (matchedMockDispatches.length === 0) {\n    throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`)\n  }\n\n  // Match method\n  matchedMockDispatches = matchedMockDispatches.filter(({ method }) => matchValue(method, key.method))\n  if (matchedMockDispatches.length === 0) {\n    throw new MockNotMatchedError(`Mock dispatch not matched for method '${key.method}' on path '${resolvedPath}'`)\n  }\n\n  // Match body\n  matchedMockDispatches = matchedMockDispatches.filter(({ body }) => typeof body !== 'undefined' ? matchValue(body, key.body) : true)\n  if (matchedMockDispatches.length === 0) {\n    throw new MockNotMatchedError(`Mock dispatch not matched for body '${key.body}' on path '${resolvedPath}'`)\n  }\n\n  // Match headers\n  matchedMockDispatches = matchedMockDispatches.filter((mockDispatch) => matchHeaders(mockDispatch, key.headers))\n  if (matchedMockDispatches.length === 0) {\n    const headers = typeof key.headers === 'object' ? JSON.stringify(key.headers) : key.headers\n    throw new MockNotMatchedError(`Mock dispatch not matched for headers '${headers}' on path '${resolvedPath}'`)\n  }\n\n  return matchedMockDispatches[0]\n}\n\nfunction addMockDispatch (mockDispatches, key, data, opts) {\n  const baseData = { timesInvoked: 0, times: 1, persist: false, consumed: false, ...opts }\n  const replyData = typeof data === 'function' ? { callback: data } : { ...data }\n  const newMockDispatch = { ...baseData, ...key, pending: true, data: { error: null, ...replyData } }\n  mockDispatches.push(newMockDispatch)\n  return newMockDispatch\n}\n\nfunction deleteMockDispatch (mockDispatches, key) {\n  const index = mockDispatches.findIndex(dispatch => {\n    if (!dispatch.consumed) {\n      return false\n    }\n    return matchKey(dispatch, key)\n  })\n  if (index !== -1) {\n    mockDispatches.splice(index, 1)\n  }\n}\n\n/**\n * @param {string} path Path to remove trailing slash from\n */\nfunction removeTrailingSlash (path) {\n  while (path.endsWith('/')) {\n    path = path.slice(0, -1)\n  }\n\n  if (path.length === 0) {\n    path = '/'\n  }\n\n  return path\n}\n\nfunction buildKey (opts) {\n  const { path, method, body, headers, query } = opts\n\n  return {\n    path,\n    method,\n    body,\n    headers,\n    query\n  }\n}\n\nfunction generateKeyValues (data) {\n  const keys = Object.keys(data)\n  const result = []\n  for (let i = 0; i < keys.length; ++i) {\n    const key = keys[i]\n    const value = data[key]\n    const name = Buffer.from(`${key}`)\n    if (Array.isArray(value)) {\n      for (let j = 0; j < value.length; ++j) {\n        result.push(name, Buffer.from(`${value[j]}`))\n      }\n    } else {\n      result.push(name, Buffer.from(`${value}`))\n    }\n  }\n  return result\n}\n\n/**\n * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status\n * @param {number} statusCode\n */\nfunction getStatusText (statusCode) {\n  return STATUS_CODES[statusCode] || 'unknown'\n}\n\nasync function getResponse (body) {\n  const buffers = []\n  for await (const data of body) {\n    buffers.push(data)\n  }\n  return Buffer.concat(buffers).toString('utf8')\n}\n\n/**\n * Mock dispatch function used to simulate undici dispatches\n */\nfunction mockDispatch (opts, handler) {\n  // Get mock dispatch from built key\n  const key = buildKey(opts)\n  const mockDispatch = getMockDispatch(this[kDispatches], key)\n\n  mockDispatch.timesInvoked++\n\n  // Here's where we resolve a callback if a callback is present for the dispatch data.\n  if (mockDispatch.data.callback) {\n    mockDispatch.data = { ...mockDispatch.data, ...mockDispatch.data.callback(opts) }\n  }\n\n  // Parse mockDispatch data\n  const { data: { statusCode, data, headers, trailers, error }, delay, persist } = mockDispatch\n  const { timesInvoked, times } = mockDispatch\n\n  // If it's used up and not persistent, mark as consumed\n  mockDispatch.consumed = !persist && timesInvoked >= times\n  mockDispatch.pending = timesInvoked < times\n\n  // If specified, trigger dispatch error\n  if (error !== null) {\n    deleteMockDispatch(this[kDispatches], key)\n    handler.onError(error)\n    return true\n  }\n\n  // Track whether the request has been aborted\n  let aborted = false\n  let timer = null\n\n  function abort (err) {\n    if (aborted) {\n      return\n    }\n    aborted = true\n\n    // Clear the pending delayed response if any\n    if (timer !== null) {\n      clearTimeout(timer)\n      timer = null\n    }\n\n    // Notify the handler of the abort\n    handler.onError(err)\n  }\n\n  // Call onConnect to allow the handler to register the abort callback\n  handler.onConnect?.(abort, null)\n\n  // Handle the request with a delay if necessary\n  if (typeof delay === 'number' && delay > 0) {\n    timer = setTimeout(() => {\n      timer = null\n      handleReply(this[kDispatches])\n    }, delay)\n  } else {\n    handleReply(this[kDispatches])\n  }\n\n  function handleReply (mockDispatches, _data = data) {\n    // Don't send response if the request was aborted\n    if (aborted) {\n      return\n    }\n\n    // fetch's HeadersList is a 1D string array\n    const optsHeaders = Array.isArray(opts.headers)\n      ? buildHeadersFromArray(opts.headers)\n      : opts.headers\n    const body = typeof _data === 'function'\n      ? _data({ ...opts, headers: optsHeaders })\n      : _data\n\n    // util.types.isPromise is likely needed for jest.\n    if (isPromise(body)) {\n      // If handleReply is asynchronous, throwing an error\n      // in the callback will reject the promise, rather than\n      // synchronously throw the error, which breaks some tests.\n      // Rather, we wait for the callback to resolve if it is a\n      // promise, and then re-run handleReply with the new body.\n      return body.then((newData) => handleReply(mockDispatches, newData))\n    }\n\n    // Check again if aborted after async body resolution\n    if (aborted) {\n      return\n    }\n\n    const responseData = getResponseData(body)\n    const responseHeaders = generateKeyValues(headers)\n    const responseTrailers = generateKeyValues(trailers)\n\n    handler.onHeaders?.(statusCode, responseHeaders, resume, getStatusText(statusCode))\n    handler.onData?.(Buffer.from(responseData))\n    handler.onComplete?.(responseTrailers)\n    deleteMockDispatch(mockDispatches, key)\n  }\n\n  function resume () {}\n\n  return true\n}\n\nfunction buildMockDispatch () {\n  const agent = this[kMockAgent]\n  const origin = this[kOrigin]\n  const originalDispatch = this[kOriginalDispatch]\n\n  return function dispatch (opts, handler) {\n    if (agent.isMockActive) {\n      try {\n        mockDispatch.call(this, opts, handler)\n      } catch (error) {\n        if (error.code === 'UND_MOCK_ERR_MOCK_NOT_MATCHED') {\n          const netConnect = agent[kGetNetConnect]()\n          if (netConnect === false) {\n            throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`)\n          }\n          if (checkNetConnect(netConnect, origin)) {\n            originalDispatch.call(this, opts, handler)\n          } else {\n            throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect is not enabled for this origin)`)\n          }\n        } else {\n          throw error\n        }\n      }\n    } else {\n      originalDispatch.call(this, opts, handler)\n    }\n  }\n}\n\nfunction checkNetConnect (netConnect, origin) {\n  const url = new URL(origin)\n  if (netConnect === true) {\n    return true\n  } else if (Array.isArray(netConnect) && netConnect.some((matcher) => matchValue(matcher, url.host))) {\n    return true\n  }\n  return false\n}\n\nfunction normalizeOrigin (origin) {\n  if (typeof origin !== 'string' && !(origin instanceof URL)) {\n    return origin\n  }\n\n  if (origin instanceof URL) {\n    return origin.origin\n  }\n\n  return origin.toLowerCase()\n}\n\nfunction buildAndValidateMockOptions (opts) {\n  const { agent, ...mockOptions } = opts\n\n  if ('enableCallHistory' in mockOptions && typeof mockOptions.enableCallHistory !== 'boolean') {\n    throw new InvalidArgumentError('options.enableCallHistory must to be a boolean')\n  }\n\n  if ('acceptNonStandardSearchParameters' in mockOptions && typeof mockOptions.acceptNonStandardSearchParameters !== 'boolean') {\n    throw new InvalidArgumentError('options.acceptNonStandardSearchParameters must to be a boolean')\n  }\n\n  if ('ignoreTrailingSlash' in mockOptions && typeof mockOptions.ignoreTrailingSlash !== 'boolean') {\n    throw new InvalidArgumentError('options.ignoreTrailingSlash must to be a boolean')\n  }\n\n  return mockOptions\n}\n\nmodule.exports = {\n  getResponseData,\n  getMockDispatch,\n  addMockDispatch,\n  deleteMockDispatch,\n  buildKey,\n  generateKeyValues,\n  matchValue,\n  getResponse,\n  getStatusText,\n  mockDispatch,\n  buildMockDispatch,\n  checkNetConnect,\n  buildAndValidateMockOptions,\n  getHeaderByName,\n  buildHeadersFromArray,\n  normalizeSearchParams,\n  normalizeOrigin\n}\n"
  },
  {
    "path": "lib/mock/pending-interceptors-formatter.js",
    "content": "'use strict'\n\nconst { Transform } = require('node:stream')\nconst { Console } = require('node:console')\n\nconst PERSISTENT = process.versions.icu ? '✅' : 'Y '\nconst NOT_PERSISTENT = process.versions.icu ? '❌' : 'N '\n\n/**\n * Gets the output of `console.table(…)` as a string.\n */\nmodule.exports = class PendingInterceptorsFormatter {\n  constructor ({ disableColors } = {}) {\n    this.transform = new Transform({\n      transform (chunk, _enc, cb) {\n        cb(null, chunk)\n      }\n    })\n\n    this.logger = new Console({\n      stdout: this.transform,\n      inspectOptions: {\n        colors: !disableColors && !process.env.CI\n      }\n    })\n  }\n\n  format (pendingInterceptors) {\n    const withPrettyHeaders = pendingInterceptors.map(\n      ({ method, path, data: { statusCode }, persist, times, timesInvoked, origin }) => ({\n        Method: method,\n        Origin: origin,\n        Path: path,\n        'Status code': statusCode,\n        Persistent: persist ? PERSISTENT : NOT_PERSISTENT,\n        Invocations: timesInvoked,\n        Remaining: persist ? Infinity : times - timesInvoked\n      }))\n\n    this.logger.table(withPrettyHeaders)\n    return this.transform.read().toString()\n  }\n}\n"
  },
  {
    "path": "lib/mock/snapshot-agent.js",
    "content": "'use strict'\n\nconst Agent = require('../dispatcher/agent')\nconst MockAgent = require('./mock-agent')\nconst { SnapshotRecorder } = require('./snapshot-recorder')\nconst WrapHandler = require('../handler/wrap-handler')\nconst { InvalidArgumentError, UndiciError } = require('../core/errors')\nconst { validateSnapshotMode } = require('./snapshot-utils')\n\nconst kSnapshotRecorder = Symbol('kSnapshotRecorder')\nconst kSnapshotMode = Symbol('kSnapshotMode')\nconst kSnapshotPath = Symbol('kSnapshotPath')\nconst kSnapshotLoaded = Symbol('kSnapshotLoaded')\nconst kRealAgent = Symbol('kRealAgent')\n\n// Static flag to ensure warning is only emitted once per process\nlet warningEmitted = false\n\nclass SnapshotAgent extends MockAgent {\n  constructor (opts = {}) {\n    // Emit experimental warning only once\n    if (!warningEmitted) {\n      process.emitWarning(\n        'SnapshotAgent is experimental and subject to change',\n        'ExperimentalWarning'\n      )\n      warningEmitted = true\n    }\n\n    const {\n      mode = 'record',\n      snapshotPath = null,\n      ...mockAgentOpts\n    } = opts\n\n    super(mockAgentOpts)\n\n    validateSnapshotMode(mode)\n\n    // Validate snapshotPath is provided when required\n    if ((mode === 'playback' || mode === 'update') && !snapshotPath) {\n      throw new InvalidArgumentError(`snapshotPath is required when mode is '${mode}'`)\n    }\n\n    this[kSnapshotMode] = mode\n    this[kSnapshotPath] = snapshotPath\n\n    this[kSnapshotRecorder] = new SnapshotRecorder({\n      snapshotPath: this[kSnapshotPath],\n      mode: this[kSnapshotMode],\n      maxSnapshots: opts.maxSnapshots,\n      autoFlush: opts.autoFlush,\n      flushInterval: opts.flushInterval,\n      matchHeaders: opts.matchHeaders,\n      ignoreHeaders: opts.ignoreHeaders,\n      excludeHeaders: opts.excludeHeaders,\n      matchBody: opts.matchBody,\n      matchQuery: opts.matchQuery,\n      caseSensitive: opts.caseSensitive,\n      shouldRecord: opts.shouldRecord,\n      shouldPlayback: opts.shouldPlayback,\n      excludeUrls: opts.excludeUrls\n    })\n    this[kSnapshotLoaded] = false\n\n    // For recording/update mode, we need a real agent to make actual requests\n    // For playback mode, we need a real agent if there are excluded URLs\n    if (this[kSnapshotMode] === 'record' || this[kSnapshotMode] === 'update' ||\n        (this[kSnapshotMode] === 'playback' && opts.excludeUrls && opts.excludeUrls.length > 0)) {\n      this[kRealAgent] = new Agent(opts)\n    }\n\n    // Auto-load snapshots in playback/update mode\n    if ((this[kSnapshotMode] === 'playback' || this[kSnapshotMode] === 'update') && this[kSnapshotPath]) {\n      this.loadSnapshots().catch(() => {\n        // Ignore load errors - file might not exist yet\n      })\n    }\n  }\n\n  dispatch (opts, handler) {\n    handler = WrapHandler.wrap(handler)\n    const mode = this[kSnapshotMode]\n\n    // Check if URL should be excluded (pass through without mocking/recording)\n    if (this[kSnapshotRecorder].isUrlExcluded(opts)) {\n      // Real agent is guaranteed by constructor when excludeUrls is configured\n      return this[kRealAgent].dispatch(opts, handler)\n    }\n\n    if (mode === 'playback' || mode === 'update') {\n      // Ensure snapshots are loaded\n      if (!this[kSnapshotLoaded]) {\n        // Need to load asynchronously, delegate to async version\n        return this.#asyncDispatch(opts, handler)\n      }\n\n      // Try to find existing snapshot (synchronous)\n      const snapshot = this[kSnapshotRecorder].findSnapshot(opts)\n\n      if (snapshot) {\n        // Use recorded response (synchronous)\n        return this.#replaySnapshot(snapshot, handler)\n      } else if (mode === 'update') {\n        // Make real request and record it (async required)\n        return this.#recordAndReplay(opts, handler)\n      } else {\n        // Playback mode but no snapshot found\n        const error = new UndiciError(`No snapshot found for ${opts.method || 'GET'} ${opts.path}`)\n        if (handler.onError) {\n          handler.onError(error)\n          return\n        }\n        throw error\n      }\n    } else if (mode === 'record') {\n      // Record mode - make real request and save response (async required)\n      return this.#recordAndReplay(opts, handler)\n    }\n  }\n\n  /**\n   * Async version of dispatch for when we need to load snapshots first\n   */\n  async #asyncDispatch (opts, handler) {\n    await this.loadSnapshots()\n    return this.dispatch(opts, handler)\n  }\n\n  /**\n   * Records a real request and replays the response\n   */\n  #recordAndReplay (opts, handler) {\n    const responseData = {\n      statusCode: null,\n      headers: {},\n      trailers: {},\n      body: []\n    }\n\n    const self = this // Capture 'this' context for use within nested handler callbacks\n\n    const recordingHandler = {\n      onRequestStart (controller, context) {\n        return handler.onRequestStart(controller, { ...context, history: this.history })\n      },\n\n      onRequestUpgrade (controller, statusCode, headers, socket) {\n        return handler.onRequestUpgrade(controller, statusCode, headers, socket)\n      },\n\n      onResponseStart (controller, statusCode, headers, statusMessage) {\n        responseData.statusCode = statusCode\n        responseData.headers = headers\n        return handler.onResponseStart(controller, statusCode, headers, statusMessage)\n      },\n\n      onResponseData (controller, chunk) {\n        responseData.body.push(chunk)\n        return handler.onResponseData(controller, chunk)\n      },\n\n      onResponseEnd (controller, trailers) {\n        responseData.trailers = trailers\n\n        // Record the interaction using captured 'self' context (fire and forget)\n        const responseBody = Buffer.concat(responseData.body)\n        self[kSnapshotRecorder].record(opts, {\n          statusCode: responseData.statusCode,\n          headers: responseData.headers,\n          body: responseBody,\n          trailers: responseData.trailers\n        })\n          .then(() => handler.onResponseEnd(controller, trailers))\n          .catch((error) => handler.onResponseError(controller, error))\n      }\n    }\n\n    // Use composed agent if available (includes interceptors), otherwise use real agent\n    const agent = this[kRealAgent]\n    return agent.dispatch(opts, recordingHandler)\n  }\n\n  /**\n   * Replays a recorded response\n   *\n   * @param {Object} snapshot - The recorded snapshot to replay.\n   * @param {Object} handler - The handler to call with the response data.\n   * @returns {void}\n   */\n  #replaySnapshot (snapshot, handler) {\n    try {\n      const { response } = snapshot\n\n      const controller = {\n        pause () { },\n        resume () { },\n        abort (reason) {\n          this.aborted = true\n          this.reason = reason\n        },\n\n        aborted: false,\n        paused: false\n      }\n\n      handler.onRequestStart(controller)\n\n      handler.onResponseStart(controller, response.statusCode, response.headers)\n\n      // Body is always stored as base64 string\n      const body = Buffer.from(response.body, 'base64')\n      handler.onResponseData(controller, body)\n\n      handler.onResponseEnd(controller, response.trailers)\n    } catch (error) {\n      handler.onError?.(error)\n    }\n  }\n\n  /**\n   * Loads snapshots from file\n   *\n   * @param {string} [filePath] - Optional file path to load snapshots from.\n   * @returns {Promise<void>} - Resolves when snapshots are loaded.\n   */\n  async loadSnapshots (filePath) {\n    await this[kSnapshotRecorder].loadSnapshots(filePath || this[kSnapshotPath])\n    this[kSnapshotLoaded] = true\n\n    // In playback mode, set up MockAgent interceptors for all snapshots\n    if (this[kSnapshotMode] === 'playback') {\n      this.#setupMockInterceptors()\n    }\n  }\n\n  /**\n   * Saves snapshots to file\n   *\n   * @param {string} [filePath] - Optional file path to save snapshots to.\n   * @returns {Promise<void>} - Resolves when snapshots are saved.\n   */\n  async saveSnapshots (filePath) {\n    return this[kSnapshotRecorder].saveSnapshots(filePath || this[kSnapshotPath])\n  }\n\n  /**\n   * Sets up MockAgent interceptors based on recorded snapshots.\n   *\n   * This method creates MockAgent interceptors for each recorded snapshot,\n   * allowing the SnapshotAgent to fall back to MockAgent's standard intercept\n   * mechanism in playback mode. Each interceptor is configured to persist\n   * (remain active for multiple requests) and responds with the recorded\n   * response data.\n   *\n   * Called automatically when loading snapshots in playback mode.\n   *\n   * @returns {void}\n   */\n  #setupMockInterceptors () {\n    for (const snapshot of this[kSnapshotRecorder].getSnapshots()) {\n      const { request, responses, response } = snapshot\n      const url = new URL(request.url)\n\n      const mockPool = this.get(url.origin)\n\n      // Handle both new format (responses array) and legacy format (response object)\n      const responseData = responses ? responses[0] : response\n      if (!responseData) continue\n\n      mockPool.intercept({\n        path: url.pathname + url.search,\n        method: request.method,\n        headers: request.headers,\n        body: request.body\n      }).reply(responseData.statusCode, responseData.body, {\n        headers: responseData.headers,\n        trailers: responseData.trailers\n      }).persist()\n    }\n  }\n\n  /**\n   * Gets the snapshot recorder\n   * @return {SnapshotRecorder} - The snapshot recorder instance\n   */\n  getRecorder () {\n    return this[kSnapshotRecorder]\n  }\n\n  /**\n   * Gets the current mode\n   * @return {import('./snapshot-utils').SnapshotMode} - The current snapshot mode\n   */\n  getMode () {\n    return this[kSnapshotMode]\n  }\n\n  /**\n   * Clears all snapshots\n   * @returns {void}\n   */\n  clearSnapshots () {\n    this[kSnapshotRecorder].clear()\n  }\n\n  /**\n   * Resets call counts for all snapshots (useful for test cleanup)\n   * @returns {void}\n   */\n  resetCallCounts () {\n    this[kSnapshotRecorder].resetCallCounts()\n  }\n\n  /**\n   * Deletes a specific snapshot by request options\n   * @param {import('./snapshot-recorder').SnapshotRequestOptions} requestOpts - Request options to identify the snapshot\n   * @return {Promise<boolean>} - Returns true if the snapshot was deleted, false if not found\n   */\n  deleteSnapshot (requestOpts) {\n    return this[kSnapshotRecorder].deleteSnapshot(requestOpts)\n  }\n\n  /**\n   * Gets information about a specific snapshot\n   * @returns {import('./snapshot-recorder').SnapshotInfo|null} - Snapshot information or null if not found\n   */\n  getSnapshotInfo (requestOpts) {\n    return this[kSnapshotRecorder].getSnapshotInfo(requestOpts)\n  }\n\n  /**\n   * Replaces all snapshots with new data (full replacement)\n   * @param {Array<{hash: string; snapshot: import('./snapshot-recorder').SnapshotEntryshotEntry}>|Record<string, import('./snapshot-recorder').SnapshotEntry>} snapshotData - New snapshot data to replace existing snapshots\n   * @returns {void}\n   */\n  replaceSnapshots (snapshotData) {\n    this[kSnapshotRecorder].replaceSnapshots(snapshotData)\n  }\n\n  /**\n   * Closes the agent, saving snapshots and cleaning up resources.\n   *\n   * @returns {Promise<void>}\n   */\n  async close () {\n    await this[kSnapshotRecorder].close()\n    await this[kRealAgent]?.close()\n    await super.close()\n  }\n}\n\nmodule.exports = SnapshotAgent\n"
  },
  {
    "path": "lib/mock/snapshot-recorder.js",
    "content": "'use strict'\n\nconst { writeFile, readFile, mkdir } = require('node:fs/promises')\nconst { dirname, resolve } = require('node:path')\nconst { setTimeout, clearTimeout } = require('node:timers')\nconst { InvalidArgumentError, UndiciError } = require('../core/errors')\nconst { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require('./snapshot-utils')\n\n/**\n * @typedef {Object} SnapshotRequestOptions\n * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)\n * @property {string} path - Request path\n * @property {string} origin - Request origin (base URL)\n * @property {import('./snapshot-utils').Headers|import('./snapshot-utils').UndiciHeaders} headers - Request headers\n * @property {import('./snapshot-utils').NormalizedHeaders} _normalizedHeaders - Request headers as a lowercase object\n * @property {string|Buffer} [body] - Request body (optional)\n */\n\n/**\n * @typedef {Object} SnapshotEntryRequest\n * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)\n * @property {string} url - Full URL of the request\n * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized headers as a lowercase object\n * @property {string|Buffer} [body] - Request body (optional)\n */\n\n/**\n * @typedef {Object} SnapshotEntryResponse\n * @property {number} statusCode - HTTP status code of the response\n * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized response headers as a lowercase object\n * @property {string} body - Response body as a base64url encoded string\n * @property {Object} [trailers] - Optional response trailers\n */\n\n/**\n * @typedef {Object} SnapshotEntry\n * @property {SnapshotEntryRequest} request - The request object\n * @property {Array<SnapshotEntryResponse>} responses - Array of response objects\n * @property {number} callCount - Number of times this snapshot has been called\n * @property {string} timestamp - ISO timestamp of when the snapshot was created\n */\n\n/**\n * @typedef {Object} SnapshotRecorderMatchOptions\n * @property {Array<string>} [matchHeaders=[]] - Headers to match (empty array means match all headers)\n * @property {Array<string>} [ignoreHeaders=[]] - Headers to ignore for matching\n * @property {Array<string>} [excludeHeaders=[]] - Headers to exclude from matching\n * @property {boolean} [matchBody=true] - Whether to match request body\n * @property {boolean} [matchQuery=true] - Whether to match query properties\n * @property {boolean} [caseSensitive=false] - Whether header matching is case-sensitive\n */\n\n/**\n * @typedef {Object} SnapshotRecorderOptions\n * @property {string} [snapshotPath] - Path to save/load snapshots\n * @property {import('./snapshot-utils').SnapshotMode} [mode='record'] - Mode: 'record' or 'playback'\n * @property {number} [maxSnapshots=Infinity] - Maximum number of snapshots to keep\n * @property {boolean} [autoFlush=false] - Whether to automatically flush snapshots to disk\n * @property {number} [flushInterval=30000] - Auto-flush interval in milliseconds (default: 30 seconds)\n * @property {Array<string|RegExp>} [excludeUrls=[]] - URLs to exclude from recording\n * @property {function} [shouldRecord=null] - Function to filter requests for recording\n * @property {function} [shouldPlayback=null] - Function to filter requests\n */\n\n/**\n * @typedef {Object} SnapshotFormattedRequest\n * @property {string} method - HTTP method (e.g. 'GET', 'POST', etc.)\n * @property {string} url - Full URL of the request (with query parameters if matchQuery is true)\n * @property {import('./snapshot-utils').NormalizedHeaders} headers - Normalized headers as a lowercase object\n * @property {string} body - Request body (optional, only if matchBody is true)\n */\n\n/**\n * @typedef {Object} SnapshotInfo\n * @property {string} hash - Hash key for the snapshot\n * @property {SnapshotEntryRequest} request - The request object\n * @property {number} responseCount - Number of responses recorded for this request\n * @property {number} callCount - Number of times this snapshot has been called\n * @property {string} timestamp - ISO timestamp of when the snapshot was created\n */\n\n/**\n * Formats a request for consistent snapshot storage\n * Caches normalized headers to avoid repeated processing\n *\n * @param {SnapshotRequestOptions} opts - Request options\n * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached header sets for performance\n * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers and body\n * @returns {SnapshotFormattedRequest} - Formatted request object\n */\nfunction formatRequestKey (opts, headerFilters, matchOptions = {}) {\n  const url = new URL(opts.path, opts.origin)\n\n  // Cache normalized headers if not already done\n  const normalized = opts._normalizedHeaders || normalizeHeaders(opts.headers)\n  if (!opts._normalizedHeaders) {\n    opts._normalizedHeaders = normalized\n  }\n\n  return {\n    method: opts.method || 'GET',\n    url: matchOptions.matchQuery !== false ? url.toString() : `${url.origin}${url.pathname}`,\n    headers: filterHeadersForMatching(normalized, headerFilters, matchOptions),\n    body: matchOptions.matchBody !== false && opts.body ? String(opts.body) : ''\n  }\n}\n\n/**\n * Filters headers based on matching configuration\n *\n * @param {import('./snapshot-utils').Headers} headers - Headers to filter\n * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached sets for ignore, exclude, and match headers\n * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers\n */\nfunction filterHeadersForMatching (headers, headerFilters, matchOptions = {}) {\n  if (!headers || typeof headers !== 'object') return {}\n\n  const {\n    caseSensitive = false\n  } = matchOptions\n\n  const filtered = {}\n  const { ignore, exclude, match } = headerFilters\n\n  for (const [key, value] of Object.entries(headers)) {\n    const headerKey = caseSensitive ? key : key.toLowerCase()\n\n    // Skip if in exclude list (for security)\n    if (exclude.has(headerKey)) continue\n\n    // Skip if in ignore list (for matching)\n    if (ignore.has(headerKey)) continue\n\n    // If matchHeaders is specified, only include those headers\n    if (match.size !== 0) {\n      if (!match.has(headerKey)) continue\n    }\n\n    filtered[headerKey] = value\n  }\n\n  return filtered\n}\n\n/**\n * Filters headers for storage (only excludes sensitive headers)\n *\n * @param {import('./snapshot-utils').Headers} headers - Headers to filter\n * @param {import('./snapshot-utils').HeaderFilters} headerFilters - Cached sets for ignore, exclude, and match headers\n * @param {SnapshotRecorderMatchOptions} [matchOptions] - Matching options for headers\n */\nfunction filterHeadersForStorage (headers, headerFilters, matchOptions = {}) {\n  if (!headers || typeof headers !== 'object') return {}\n\n  const {\n    caseSensitive = false\n  } = matchOptions\n\n  const filtered = {}\n  const { exclude: excludeSet } = headerFilters\n\n  for (const [key, value] of Object.entries(headers)) {\n    const headerKey = caseSensitive ? key : key.toLowerCase()\n\n    // Skip if in exclude list (for security)\n    if (excludeSet.has(headerKey)) continue\n\n    filtered[headerKey] = value\n  }\n\n  return filtered\n}\n\n/**\n * Creates a hash key for request matching\n * Properly orders headers to avoid conflicts and uses crypto hashing when available\n *\n * @param {SnapshotFormattedRequest} formattedRequest - Request object\n * @returns {string} - Base64url encoded hash of the request\n */\nfunction createRequestHash (formattedRequest) {\n  const parts = [\n    formattedRequest.method,\n    formattedRequest.url\n  ]\n\n  // Process headers in a deterministic way to avoid conflicts\n  if (formattedRequest.headers && typeof formattedRequest.headers === 'object') {\n    const headerKeys = Object.keys(formattedRequest.headers).sort()\n    for (const key of headerKeys) {\n      const values = Array.isArray(formattedRequest.headers[key])\n        ? formattedRequest.headers[key]\n        : [formattedRequest.headers[key]]\n\n      // Add header name\n      parts.push(key)\n\n      // Add all values for this header, sorted for consistency\n      for (const value of values.sort()) {\n        parts.push(String(value))\n      }\n    }\n  }\n\n  // Add body\n  parts.push(formattedRequest.body)\n\n  const content = parts.join('|')\n\n  return hashId(content)\n}\n\nclass SnapshotRecorder {\n  /** @type {NodeJS.Timeout | null} */\n  #flushTimeout\n\n  /** @type {import('./snapshot-utils').IsUrlExcluded} */\n  #isUrlExcluded\n\n  /** @type {Map<string, SnapshotEntry>} */\n  #snapshots = new Map()\n\n  /** @type {string|undefined} */\n  #snapshotPath\n\n  /** @type {number} */\n  #maxSnapshots = Infinity\n\n  /** @type {boolean} */\n  #autoFlush = false\n\n  /** @type {import('./snapshot-utils').HeaderFilters} */\n  #headerFilters\n\n  /**\n   * Creates a new SnapshotRecorder instance\n   * @param {SnapshotRecorderOptions&SnapshotRecorderMatchOptions} [options={}] - Configuration options for the recorder\n   */\n  constructor (options = {}) {\n    this.#snapshotPath = options.snapshotPath\n    this.#maxSnapshots = options.maxSnapshots || Infinity\n    this.#autoFlush = options.autoFlush || false\n    this.flushInterval = options.flushInterval || 30000 // 30 seconds default\n    this._flushTimer = null\n\n    // Matching configuration\n    /** @type {Required<SnapshotRecorderMatchOptions>} */\n    this.matchOptions = {\n      matchHeaders: options.matchHeaders || [], // empty means match all headers\n      ignoreHeaders: options.ignoreHeaders || [],\n      excludeHeaders: options.excludeHeaders || [],\n      matchBody: options.matchBody !== false, // default: true\n      matchQuery: options.matchQuery !== false, // default: true\n      caseSensitive: options.caseSensitive || false\n    }\n\n    // Cache processed header sets to avoid recreating them on every request\n    this.#headerFilters = createHeaderFilters(this.matchOptions)\n\n    // Request filtering callbacks\n    this.shouldRecord = options.shouldRecord || (() => true) // function(requestOpts) -> boolean\n    this.shouldPlayback = options.shouldPlayback || (() => true) // function(requestOpts) -> boolean\n\n    // URL pattern filtering\n    this.#isUrlExcluded = isUrlExcludedFactory(options.excludeUrls) // Array of regex patterns or strings\n\n    // Start auto-flush timer if enabled\n    if (this.#autoFlush && this.#snapshotPath) {\n      this.#startAutoFlush()\n    }\n  }\n\n  /**\n   * Records a request-response interaction\n   * @param {SnapshotRequestOptions} requestOpts - Request options\n   * @param {SnapshotEntryResponse} response - Response data to record\n   * @return {Promise<void>} - Resolves when the recording is complete\n   */\n  async record (requestOpts, response) {\n    // Check if recording should be filtered out\n    if (!this.shouldRecord(requestOpts)) {\n      return // Skip recording\n    }\n\n    // Check URL exclusion patterns\n    if (this.isUrlExcluded(requestOpts)) {\n      return // Skip recording\n    }\n\n    const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)\n    const hash = createRequestHash(request)\n\n    // Extract response data - always store body as base64\n    const normalizedHeaders = normalizeHeaders(response.headers)\n\n    /** @type {SnapshotEntryResponse} */\n    const responseData = {\n      statusCode: response.statusCode,\n      headers: filterHeadersForStorage(normalizedHeaders, this.#headerFilters, this.matchOptions),\n      body: Buffer.isBuffer(response.body)\n        ? response.body.toString('base64')\n        : Buffer.from(String(response.body || '')).toString('base64'),\n      trailers: response.trailers\n    }\n\n    // Remove oldest snapshot if we exceed maxSnapshots limit\n    if (this.#snapshots.size >= this.#maxSnapshots && !this.#snapshots.has(hash)) {\n      const oldestKey = this.#snapshots.keys().next().value\n      this.#snapshots.delete(oldestKey)\n    }\n\n    // Support sequential responses - if snapshot exists, add to responses array\n    const existingSnapshot = this.#snapshots.get(hash)\n    if (existingSnapshot && existingSnapshot.responses) {\n      existingSnapshot.responses.push(responseData)\n      existingSnapshot.timestamp = new Date().toISOString()\n    } else {\n      this.#snapshots.set(hash, {\n        request,\n        responses: [responseData], // Always store as array for consistency\n        callCount: 0,\n        timestamp: new Date().toISOString()\n      })\n    }\n\n    // Auto-flush if enabled\n    if (this.#autoFlush && this.#snapshotPath) {\n      this.#scheduleFlush()\n    }\n  }\n\n  /**\n   * Checks if a URL should be excluded from recording/playback\n   * @param {SnapshotRequestOptions} requestOpts - Request options to check\n   * @returns {boolean} - True if URL is excluded\n   */\n  isUrlExcluded (requestOpts) {\n    const url = new URL(requestOpts.path, requestOpts.origin).toString()\n    return this.#isUrlExcluded(url)\n  }\n\n  /**\n   * Finds a matching snapshot for the given request\n   * Returns the appropriate response based on call count for sequential responses\n   *\n   * @param {SnapshotRequestOptions} requestOpts - Request options to match\n   * @returns {SnapshotEntry&Record<'response', SnapshotEntryResponse>|undefined} - Matching snapshot response or undefined if not found\n   */\n  findSnapshot (requestOpts) {\n    // Check if playback should be filtered out\n    if (!this.shouldPlayback(requestOpts)) {\n      return undefined // Skip playback\n    }\n\n    // Check URL exclusion patterns\n    if (this.isUrlExcluded(requestOpts)) {\n      return undefined // Skip playback\n    }\n\n    const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)\n    const hash = createRequestHash(request)\n    const snapshot = this.#snapshots.get(hash)\n\n    if (!snapshot) return undefined\n\n    // Handle sequential responses\n    const currentCallCount = snapshot.callCount || 0\n    const responseIndex = Math.min(currentCallCount, snapshot.responses.length - 1)\n    snapshot.callCount = currentCallCount + 1\n\n    return {\n      ...snapshot,\n      response: snapshot.responses[responseIndex]\n    }\n  }\n\n  /**\n   * Loads snapshots from file\n   * @param {string} [filePath] - Optional file path to load snapshots from\n   * @return {Promise<void>} - Resolves when snapshots are loaded\n   */\n  async loadSnapshots (filePath) {\n    const path = filePath || this.#snapshotPath\n    if (!path) {\n      throw new InvalidArgumentError('Snapshot path is required')\n    }\n\n    try {\n      const data = await readFile(resolve(path), 'utf8')\n      const parsed = JSON.parse(data)\n\n      // Convert array format back to Map\n      if (Array.isArray(parsed)) {\n        this.#snapshots.clear()\n        for (const { hash, snapshot } of parsed) {\n          this.#snapshots.set(hash, snapshot)\n        }\n      } else {\n        // Legacy object format\n        this.#snapshots = new Map(Object.entries(parsed))\n      }\n    } catch (error) {\n      if (error.code === 'ENOENT') {\n        // File doesn't exist yet - that's ok for recording mode\n        this.#snapshots.clear()\n      } else {\n        throw new UndiciError(`Failed to load snapshots from ${path}`, { cause: error })\n      }\n    }\n  }\n\n  /**\n   * Saves snapshots to file\n   *\n   * @param {string} [filePath] - Optional file path to save snapshots\n   * @returns {Promise<void>} - Resolves when snapshots are saved\n   */\n  async saveSnapshots (filePath) {\n    const path = filePath || this.#snapshotPath\n    if (!path) {\n      throw new InvalidArgumentError('Snapshot path is required')\n    }\n\n    const resolvedPath = resolve(path)\n\n    // Ensure directory exists\n    await mkdir(dirname(resolvedPath), { recursive: true })\n\n    // Convert Map to serializable format\n    const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({\n      hash,\n      snapshot\n    }))\n\n    await writeFile(resolvedPath, JSON.stringify(data, null, 2), { flush: true })\n  }\n\n  /**\n   * Clears all recorded snapshots\n   * @returns {void}\n   */\n  clear () {\n    this.#snapshots.clear()\n  }\n\n  /**\n   * Gets all recorded snapshots\n   * @return {Array<SnapshotEntry>} - Array of all recorded snapshots\n   */\n  getSnapshots () {\n    return Array.from(this.#snapshots.values())\n  }\n\n  /**\n   * Gets snapshot count\n   * @return {number} - Number of recorded snapshots\n   */\n  size () {\n    return this.#snapshots.size\n  }\n\n  /**\n   * Resets call counts for all snapshots (useful for test cleanup)\n   * @returns {void}\n   */\n  resetCallCounts () {\n    for (const snapshot of this.#snapshots.values()) {\n      snapshot.callCount = 0\n    }\n  }\n\n  /**\n   * Deletes a specific snapshot by request options\n   * @param {SnapshotRequestOptions} requestOpts - Request options to match\n   * @returns {boolean} - True if snapshot was deleted, false if not found\n   */\n  deleteSnapshot (requestOpts) {\n    const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)\n    const hash = createRequestHash(request)\n    return this.#snapshots.delete(hash)\n  }\n\n  /**\n   * Gets information about a specific snapshot\n   * @param {SnapshotRequestOptions} requestOpts - Request options to match\n   * @returns {SnapshotInfo|null} - Snapshot information or null if not found\n   */\n  getSnapshotInfo (requestOpts) {\n    const request = formatRequestKey(requestOpts, this.#headerFilters, this.matchOptions)\n    const hash = createRequestHash(request)\n    const snapshot = this.#snapshots.get(hash)\n\n    if (!snapshot) return null\n\n    return {\n      hash,\n      request: snapshot.request,\n      responseCount: snapshot.responses ? snapshot.responses.length : (snapshot.response ? 1 : 0), // .response for legacy snapshots\n      callCount: snapshot.callCount || 0,\n      timestamp: snapshot.timestamp\n    }\n  }\n\n  /**\n   * Replaces all snapshots with new data (full replacement)\n   * @param {Array<{hash: string; snapshot: SnapshotEntry}>|Record<string, SnapshotEntry>} snapshotData - New snapshot data to replace existing ones\n   * @returns {void}\n   */\n  replaceSnapshots (snapshotData) {\n    this.#snapshots.clear()\n\n    if (Array.isArray(snapshotData)) {\n      for (const { hash, snapshot } of snapshotData) {\n        this.#snapshots.set(hash, snapshot)\n      }\n    } else if (snapshotData && typeof snapshotData === 'object') {\n      // Legacy object format\n      this.#snapshots = new Map(Object.entries(snapshotData))\n    }\n  }\n\n  /**\n   * Starts the auto-flush timer\n   * @returns {void}\n   */\n  #startAutoFlush () {\n    return this.#scheduleFlush()\n  }\n\n  /**\n   * Stops the auto-flush timer\n   * @returns {void}\n   */\n  #stopAutoFlush () {\n    if (this.#flushTimeout) {\n      clearTimeout(this.#flushTimeout)\n      // Ensure any pending flush is completed\n      this.saveSnapshots().catch(() => {\n      // Ignore flush errors\n      })\n      this.#flushTimeout = null\n    }\n  }\n\n  /**\n   * Schedules a flush (debounced to avoid excessive writes)\n   */\n  #scheduleFlush () {\n    this.#flushTimeout = setTimeout(() => {\n      this.saveSnapshots().catch(() => {\n        // Ignore flush errors\n      })\n      if (this.#autoFlush) {\n        this.#flushTimeout?.refresh()\n      } else {\n        this.#flushTimeout = null\n      }\n    }, 1000) // 1 second debounce\n  }\n\n  /**\n   * Cleanup method to stop timers\n   * @returns {void}\n   */\n  destroy () {\n    this.#stopAutoFlush()\n    if (this.#flushTimeout) {\n      clearTimeout(this.#flushTimeout)\n      this.#flushTimeout = null\n    }\n  }\n\n  /**\n   * Async close method that saves all recordings and performs cleanup\n   * @returns {Promise<void>}\n   */\n  async close () {\n    // Save any pending recordings if we have a snapshot path\n    if (this.#snapshotPath && this.#snapshots.size !== 0) {\n      await this.saveSnapshots()\n    }\n\n    // Perform cleanup\n    this.destroy()\n  }\n}\n\nmodule.exports = { SnapshotRecorder, formatRequestKey, createRequestHash, filterHeadersForMatching, filterHeadersForStorage, createHeaderFilters }\n"
  },
  {
    "path": "lib/mock/snapshot-utils.js",
    "content": "'use strict'\n\nconst { InvalidArgumentError } = require('../core/errors')\nconst { runtimeFeatures } = require('../util/runtime-features.js')\n\n/**\n * @typedef {Object} HeaderFilters\n * @property {Set<string>} ignore - Set of headers to ignore for matching\n * @property {Set<string>} exclude - Set of headers to exclude from matching\n * @property {Set<string>} match - Set of headers to match (empty means match\n */\n\n/**\n * Creates cached header sets for performance\n *\n * @param {import('./snapshot-recorder').SnapshotRecorderMatchOptions} matchOptions - Matching options for headers\n * @returns {HeaderFilters} - Cached sets for ignore, exclude, and match headers\n */\nfunction createHeaderFilters (matchOptions = {}) {\n  const { ignoreHeaders = [], excludeHeaders = [], matchHeaders = [], caseSensitive = false } = matchOptions\n\n  return {\n    ignore: new Set(ignoreHeaders.map(header => caseSensitive ? header : header.toLowerCase())),\n    exclude: new Set(excludeHeaders.map(header => caseSensitive ? header : header.toLowerCase())),\n    match: new Set(matchHeaders.map(header => caseSensitive ? header : header.toLowerCase()))\n  }\n}\n\nconst crypto = runtimeFeatures.has('crypto')\n  ? require('node:crypto')\n  : null\n\n/**\n * @callback HashIdFunction\n * @param {string} value - The value to hash\n * @returns {string} - The base64url encoded hash of the value\n */\n\n/**\n * Generates a hash for a given value\n * @type {HashIdFunction}\n */\nconst hashId = crypto?.hash\n  ? (value) => crypto.hash('sha256', value, 'base64url')\n  : (value) => Buffer.from(value).toString('base64url')\n\n/**\n * @typedef {(url: string) => boolean} IsUrlExcluded Checks if a URL matches any of the exclude patterns\n */\n\n/** @typedef {{[key: Lowercase<string>]: string}} NormalizedHeaders */\n/** @typedef {Array<string>} UndiciHeaders */\n/** @typedef {Record<string, string|string[]>} Headers */\n\n/**\n * @param {*} headers\n * @returns {headers is UndiciHeaders}\n */\nfunction isUndiciHeaders (headers) {\n  return Array.isArray(headers) && (headers.length & 1) === 0\n}\n\n/**\n * Factory function to create a URL exclusion checker\n * @param {Array<string| RegExp>} [excludePatterns=[]] - Array of patterns to exclude\n * @returns {IsUrlExcluded} - A function that checks if a URL matches any of the exclude patterns\n */\nfunction isUrlExcludedFactory (excludePatterns = []) {\n  if (excludePatterns.length === 0) {\n    return () => false\n  }\n\n  return function isUrlExcluded (url) {\n    let urlLowerCased\n\n    for (const pattern of excludePatterns) {\n      if (typeof pattern === 'string') {\n        if (!urlLowerCased) {\n          // Convert URL to lowercase only once\n          urlLowerCased = url.toLowerCase()\n        }\n        // Simple string match (case-insensitive)\n        if (urlLowerCased.includes(pattern.toLowerCase())) {\n          return true\n        }\n      } else if (pattern instanceof RegExp) {\n        // Regex pattern match\n        if (pattern.test(url)) {\n          return true\n        }\n      }\n    }\n\n    return false\n  }\n}\n\n/**\n * Normalizes headers for consistent comparison\n *\n * @param {Object|UndiciHeaders} headers - Headers to normalize\n * @returns {NormalizedHeaders} - Normalized headers as a lowercase object\n */\nfunction normalizeHeaders (headers) {\n  /** @type {NormalizedHeaders} */\n  const normalizedHeaders = {}\n\n  if (!headers) return normalizedHeaders\n\n  // Handle array format (undici internal format: [name, value, name, value, ...])\n  if (isUndiciHeaders(headers)) {\n    for (let i = 0; i < headers.length; i += 2) {\n      const key = headers[i]\n      const value = headers[i + 1]\n      if (key && value !== undefined) {\n        // Convert Buffers to strings if needed\n        const keyStr = Buffer.isBuffer(key) ? key.toString() : key\n        const valueStr = Buffer.isBuffer(value) ? value.toString() : value\n        normalizedHeaders[keyStr.toLowerCase()] = valueStr\n      }\n    }\n    return normalizedHeaders\n  }\n\n  // Handle object format\n  if (headers && typeof headers === 'object') {\n    for (const [key, value] of Object.entries(headers)) {\n      if (key && typeof key === 'string') {\n        normalizedHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value)\n      }\n    }\n  }\n\n  return normalizedHeaders\n}\n\nconst validSnapshotModes = /** @type {const} */ (['record', 'playback', 'update'])\n\n/** @typedef {typeof validSnapshotModes[number]} SnapshotMode */\n\n/**\n * @param {*} mode - The snapshot mode to validate\n * @returns {asserts mode is SnapshotMode}\n */\nfunction validateSnapshotMode (mode) {\n  if (!validSnapshotModes.includes(mode)) {\n    throw new InvalidArgumentError(`Invalid snapshot mode: ${mode}. Must be one of: ${validSnapshotModes.join(', ')}`)\n  }\n}\n\nmodule.exports = {\n  createHeaderFilters,\n  hashId,\n  isUndiciHeaders,\n  normalizeHeaders,\n  isUrlExcludedFactory,\n  validateSnapshotMode\n}\n"
  },
  {
    "path": "lib/util/cache.js",
    "content": "'use strict'\n\nconst {\n  safeHTTPMethods,\n  pathHasQueryOrFragment,\n  hasSafeIterator\n} = require('../core/util')\n\nconst { serializePathWithQuery } = require('../core/util')\n\n/**\n * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts\n */\nfunction makeCacheKey (opts) {\n  if (!opts.origin) {\n    throw new Error('opts.origin is undefined')\n  }\n\n  let fullPath = opts.path || '/'\n\n  if (opts.query && !pathHasQueryOrFragment(opts.path)) {\n    fullPath = serializePathWithQuery(fullPath, opts.query)\n  }\n\n  return {\n    origin: opts.origin.toString(),\n    method: opts.method,\n    path: fullPath,\n    headers: opts.headers\n  }\n}\n\n/**\n * @param {Record<string, string[] | string>}\n * @returns {Record<string, string[] | string>}\n */\nfunction normalizeHeaders (opts) {\n  let headers\n  if (opts.headers == null) {\n    headers = {}\n  } else if (typeof opts.headers === 'object') {\n    headers = {}\n\n    if (hasSafeIterator(opts.headers)) {\n      for (const x of opts.headers) {\n        if (!Array.isArray(x)) {\n          throw new Error('opts.headers is not a valid header map')\n        }\n        const [key, val] = x\n        if (typeof key !== 'string' || typeof val !== 'string') {\n          throw new Error('opts.headers is not a valid header map')\n        }\n        headers[key.toLowerCase()] = val\n      }\n    } else {\n      for (const key of Object.keys(opts.headers)) {\n        headers[key.toLowerCase()] = opts.headers[key]\n      }\n    }\n  } else {\n    throw new Error('opts.headers is not an object')\n  }\n\n  return headers\n}\n\n/**\n * @param {any} key\n */\nfunction assertCacheKey (key) {\n  if (typeof key !== 'object') {\n    throw new TypeError(`expected key to be object, got ${typeof key}`)\n  }\n\n  for (const property of ['origin', 'method', 'path']) {\n    if (typeof key[property] !== 'string') {\n      throw new TypeError(`expected key.${property} to be string, got ${typeof key[property]}`)\n    }\n  }\n\n  if (key.headers !== undefined && typeof key.headers !== 'object') {\n    throw new TypeError(`expected headers to be object, got ${typeof key}`)\n  }\n}\n\n/**\n * @param {any} value\n */\nfunction assertCacheValue (value) {\n  if (typeof value !== 'object') {\n    throw new TypeError(`expected value to be object, got ${typeof value}`)\n  }\n\n  for (const property of ['statusCode', 'cachedAt', 'staleAt', 'deleteAt']) {\n    if (typeof value[property] !== 'number') {\n      throw new TypeError(`expected value.${property} to be number, got ${typeof value[property]}`)\n    }\n  }\n\n  if (typeof value.statusMessage !== 'string') {\n    throw new TypeError(`expected value.statusMessage to be string, got ${typeof value.statusMessage}`)\n  }\n\n  if (value.headers != null && typeof value.headers !== 'object') {\n    throw new TypeError(`expected value.rawHeaders to be object, got ${typeof value.headers}`)\n  }\n\n  if (value.vary !== undefined && typeof value.vary !== 'object') {\n    throw new TypeError(`expected value.vary to be object, got ${typeof value.vary}`)\n  }\n\n  if (value.etag !== undefined && typeof value.etag !== 'string') {\n    throw new TypeError(`expected value.etag to be string, got ${typeof value.etag}`)\n  }\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-cache-control\n * @see https://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml\n\n * @param {string | string[]} header\n * @returns {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}\n */\nfunction parseCacheControlHeader (header) {\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}\n   */\n  const output = {}\n\n  let directives\n  if (Array.isArray(header)) {\n    directives = []\n\n    for (const directive of header) {\n      directives.push(...directive.split(','))\n    }\n  } else {\n    directives = header.split(',')\n  }\n\n  for (let i = 0; i < directives.length; i++) {\n    const directive = directives[i].toLowerCase()\n    const keyValueDelimiter = directive.indexOf('=')\n\n    let key\n    let value\n    if (keyValueDelimiter !== -1) {\n      key = directive.substring(0, keyValueDelimiter).trimStart()\n      value = directive.substring(keyValueDelimiter + 1)\n    } else {\n      key = directive.trim()\n    }\n\n    switch (key) {\n      case 'min-fresh':\n      case 'max-stale':\n      case 'max-age':\n      case 's-maxage':\n      case 'stale-while-revalidate':\n      case 'stale-if-error': {\n        if (value === undefined || value[0] === ' ') {\n          continue\n        }\n\n        if (\n          value.length >= 2 &&\n          value[0] === '\"' &&\n          value[value.length - 1] === '\"'\n        ) {\n          value = value.substring(1, value.length - 1)\n        }\n\n        const parsedValue = parseInt(value, 10)\n        // eslint-disable-next-line no-self-compare\n        if (parsedValue !== parsedValue) {\n          continue\n        }\n\n        if (key === 'max-age' && key in output && output[key] >= parsedValue) {\n          continue\n        }\n\n        output[key] = parsedValue\n\n        break\n      }\n      case 'private':\n      case 'no-cache': {\n        if (value) {\n          // The private and no-cache directives can be unqualified (aka just\n          //  `private` or `no-cache`) or qualified (w/ a value). When they're\n          //  qualified, it's a list of headers like `no-cache=header1`,\n          //  `no-cache=\"header1\"`, or `no-cache=\"header1, header2\"`\n          // If we're given multiple headers, the comma messes us up since\n          //  we split the full header by commas. So, let's loop through the\n          //  remaining parts in front of us until we find one that ends in a\n          //  quote. We can then just splice all of the parts in between the\n          //  starting quote and the ending quote out of the directives array\n          //  and continue parsing like normal.\n          // https://www.rfc-editor.org/rfc/rfc9111.html#name-no-cache-2\n          if (value[0] === '\"') {\n            // Something like `no-cache=\"some-header\"` OR `no-cache=\"some-header, another-header\"`.\n\n            // Add the first header on and cut off the leading quote\n            const headers = [value.substring(1)]\n\n            let foundEndingQuote = value[value.length - 1] === '\"'\n            if (!foundEndingQuote) {\n              // Something like `no-cache=\"some-header, another-header\"`\n              //  This can still be something invalid, e.g. `no-cache=\"some-header, ...`\n              for (let j = i + 1; j < directives.length; j++) {\n                const nextPart = directives[j]\n                const nextPartLength = nextPart.length\n\n                headers.push(nextPart.trim())\n\n                if (nextPartLength !== 0 && nextPart[nextPartLength - 1] === '\"') {\n                  foundEndingQuote = true\n                  break\n                }\n              }\n            }\n\n            if (foundEndingQuote) {\n              let lastHeader = headers[headers.length - 1]\n              if (lastHeader[lastHeader.length - 1] === '\"') {\n                lastHeader = lastHeader.substring(0, lastHeader.length - 1)\n                headers[headers.length - 1] = lastHeader\n              }\n\n              if (key in output) {\n                output[key] = output[key].concat(headers)\n              } else {\n                output[key] = headers\n              }\n            }\n          } else {\n            // Something like `no-cache=\"some-header\"`\n            if (key in output) {\n              output[key] = output[key].concat(value)\n            } else {\n              output[key] = [value]\n            }\n          }\n\n          break\n        }\n      }\n      // eslint-disable-next-line no-fallthrough\n      case 'public':\n      case 'no-store':\n      case 'must-revalidate':\n      case 'proxy-revalidate':\n      case 'immutable':\n      case 'no-transform':\n      case 'must-understand':\n      case 'only-if-cached':\n        if (value) {\n          // These are qualified (something like `public=...`) when they aren't\n          //  allowed to be, skip\n          continue\n        }\n\n        output[key] = true\n        break\n      default:\n        // Ignore unknown directives as per https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.3-1\n        continue\n    }\n  }\n\n  return output\n}\n\n/**\n * @param {string | string[]} varyHeader Vary header from the server\n * @param {Record<string, string | string[]>} headers Request headers\n * @returns {Record<string, string | string[]>}\n */\nfunction parseVaryHeader (varyHeader, headers) {\n  if (typeof varyHeader === 'string' && varyHeader.includes('*')) {\n    return headers\n  }\n\n  const output = /** @type {Record<string, string | string[] | null>} */ ({})\n\n  const varyingHeaders = typeof varyHeader === 'string'\n    ? varyHeader.split(',')\n    : varyHeader\n\n  for (const header of varyingHeaders) {\n    const trimmedHeader = header.trim().toLowerCase()\n\n    output[trimmedHeader] = headers[trimmedHeader] ?? null\n  }\n\n  return output\n}\n\n/**\n * Note: this deviates from the spec a little. Empty etags (\"\", W/\"\") are valid,\n *  however, including them in cached resposnes serves little to no purpose.\n *\n * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-etag\n *\n * @param {string} etag\n * @returns {boolean}\n */\nfunction isEtagUsable (etag) {\n  if (etag.length <= 2) {\n    // Shortest an etag can be is two chars (just \"\"). This is where we deviate\n    //  from the spec requiring a min of 3 chars however\n    return false\n  }\n\n  if (etag[0] === '\"' && etag[etag.length - 1] === '\"') {\n    // ETag: \"\"asd123\"\" or ETag: \"W/\"asd123\"\", kinda undefined behavior in the\n    //  spec. Some servers will accept these while others don't.\n    // ETag: \"asd123\"\n    return !(etag[1] === '\"' || etag.startsWith('\"W/'))\n  }\n\n  if (etag.startsWith('W/\"') && etag[etag.length - 1] === '\"') {\n    // ETag: W/\"\", also where we deviate from the spec & require a min of 3\n    //  chars\n    // ETag: for W/\"\", W/\"asd123\"\n    return etag.length !== 4\n  }\n\n  // Anything else\n  return false\n}\n\n/**\n * @param {unknown} store\n * @returns {asserts store is import('../../types/cache-interceptor.d.ts').default.CacheStore}\n */\nfunction assertCacheStore (store, name = 'CacheStore') {\n  if (typeof store !== 'object' || store === null) {\n    throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store === null ? 'null' : typeof store}`)\n  }\n\n  for (const fn of ['get', 'createWriteStream', 'delete']) {\n    if (typeof store[fn] !== 'function') {\n      throw new TypeError(`${name} needs to have a \\`${fn}()\\` function`)\n    }\n  }\n}\n/**\n * @param {unknown} methods\n * @returns {asserts methods is import('../../types/cache-interceptor.d.ts').default.CacheMethods[]}\n */\nfunction assertCacheMethods (methods, name = 'CacheMethods') {\n  if (!Array.isArray(methods)) {\n    throw new TypeError(`expected type of ${name} needs to be an array, got ${methods === null ? 'null' : typeof methods}`)\n  }\n\n  if (methods.length === 0) {\n    throw new TypeError(`${name} needs to have at least one method`)\n  }\n\n  for (const method of methods) {\n    if (!safeHTTPMethods.includes(method)) {\n      throw new TypeError(`element of ${name}-array needs to be one of following values: ${safeHTTPMethods.join(', ')}, got ${method}`)\n    }\n  }\n}\n\n/**\n * Creates a string key for request deduplication purposes.\n * This key is used to identify in-flight requests that can be shared.\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey\n * @param {Set<string>} [excludeHeaders] Set of lowercase header names to exclude from the key\n * @returns {string}\n */\nfunction makeDeduplicationKey (cacheKey, excludeHeaders) {\n  // Create a deterministic string key from the cache key\n  // Include origin, method, path, and sorted headers\n  let key = `${cacheKey.origin}:${cacheKey.method}:${cacheKey.path}`\n\n  if (cacheKey.headers) {\n    const sortedHeaders = Object.keys(cacheKey.headers).sort()\n    for (const header of sortedHeaders) {\n      // Skip excluded headers\n      if (excludeHeaders?.has(header.toLowerCase())) {\n        continue\n      }\n      const value = cacheKey.headers[header]\n      key += `:${header}=${Array.isArray(value) ? value.join(',') : value}`\n    }\n  }\n\n  return key\n}\n\nmodule.exports = {\n  makeCacheKey,\n  normalizeHeaders,\n  assertCacheKey,\n  assertCacheValue,\n  parseCacheControlHeader,\n  parseVaryHeader,\n  isEtagUsable,\n  assertCacheMethods,\n  assertCacheStore,\n  makeDeduplicationKey\n}\n"
  },
  {
    "path": "lib/util/date.js",
    "content": "'use strict'\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-date-time-formats\n *\n * @param {string} date\n * @returns {Date | undefined}\n */\nfunction parseHttpDate (date) {\n  // Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate\n  // Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format\n  // Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format\n\n  switch (date[3]) {\n    case ',': return parseImfDate(date)\n    case ' ': return parseAscTimeDate(date)\n    default: return parseRfc850Date(date)\n  }\n}\n\n/**\n * @see https://httpwg.org/specs/rfc9110.html#preferred.date.format\n *\n * @param {string} date\n * @returns {Date | undefined}\n */\nfunction parseImfDate (date) {\n  if (\n    date.length !== 29 ||\n    date[4] !== ' ' ||\n    date[7] !== ' ' ||\n    date[11] !== ' ' ||\n    date[16] !== ' ' ||\n    date[19] !== ':' ||\n    date[22] !== ':' ||\n    date[25] !== ' ' ||\n    date[26] !== 'G' ||\n    date[27] !== 'M' ||\n    date[28] !== 'T'\n  ) {\n    return undefined\n  }\n\n  let weekday = -1\n  if (date[0] === 'S' && date[1] === 'u' && date[2] === 'n') { // Sunday\n    weekday = 0\n  } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n') { // Monday\n    weekday = 1\n  } else if (date[0] === 'T' && date[1] === 'u' && date[2] === 'e') { // Tuesday\n    weekday = 2\n  } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd') { // Wednesday\n    weekday = 3\n  } else if (date[0] === 'T' && date[1] === 'h' && date[2] === 'u') { // Thursday\n    weekday = 4\n  } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i') { // Friday\n    weekday = 5\n  } else if (date[0] === 'S' && date[1] === 'a' && date[2] === 't') { // Saturday\n    weekday = 6\n  } else {\n    return undefined // Not a valid day of the week\n  }\n\n  let day = 0\n  if (date[5] === '0') {\n    // Single digit day, e.g. \"Sun Nov 6 08:49:37 1994\"\n    const code = date.charCodeAt(6)\n    if (code < 49 || code > 57) {\n      return undefined // Not a digit\n    }\n    day = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(5)\n    if (code1 < 49 || code1 > 51) {\n      return undefined // Not a digit between 1 and 3\n    }\n    const code2 = date.charCodeAt(6)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let monthIdx = -1\n  if (\n    (date[8] === 'J' && date[9] === 'a' && date[10] === 'n')\n  ) {\n    monthIdx = 0 // Jan\n  } else if (\n    (date[8] === 'F' && date[9] === 'e' && date[10] === 'b')\n  ) {\n    monthIdx = 1 // Feb\n  } else if (\n    (date[8] === 'M' && date[9] === 'a')\n  ) {\n    if (date[10] === 'r') {\n      monthIdx = 2 // Mar\n    } else if (date[10] === 'y') {\n      monthIdx = 4 // May\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[8] === 'J')\n  ) {\n    if (date[9] === 'a' && date[10] === 'n') {\n      monthIdx = 0 // Jan\n    } else if (date[9] === 'u') {\n      if (date[10] === 'n') {\n        monthIdx = 5 // Jun\n      } else if (date[10] === 'l') {\n        monthIdx = 6 // Jul\n      } else {\n        return undefined // Invalid month\n      }\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[8] === 'A')\n  ) {\n    if (date[9] === 'p' && date[10] === 'r') {\n      monthIdx = 3 // Apr\n    } else if (date[9] === 'u' && date[10] === 'g') {\n      monthIdx = 7 // Aug\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[8] === 'S' && date[9] === 'e' && date[10] === 'p')\n  ) {\n    monthIdx = 8 // Sep\n  } else if (\n    (date[8] === 'O' && date[9] === 'c' && date[10] === 't')\n  ) {\n    monthIdx = 9 // Oct\n  } else if (\n    (date[8] === 'N' && date[9] === 'o' && date[10] === 'v')\n  ) {\n    monthIdx = 10 // Nov\n  } else if (\n    (date[8] === 'D' && date[9] === 'e' && date[10] === 'c')\n  ) {\n    monthIdx = 11 // Dec\n  } else {\n    // Not a valid month\n    return undefined\n  }\n\n  const yearDigit1 = date.charCodeAt(12)\n  if (yearDigit1 < 48 || yearDigit1 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit2 = date.charCodeAt(13)\n  if (yearDigit2 < 48 || yearDigit2 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit3 = date.charCodeAt(14)\n  if (yearDigit3 < 48 || yearDigit3 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit4 = date.charCodeAt(15)\n  if (yearDigit4 < 48 || yearDigit4 > 57) {\n    return undefined // Not a digit\n  }\n  const year = (yearDigit1 - 48) * 1000 + (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48)\n\n  let hour = 0\n  if (date[17] === '0') {\n    const code = date.charCodeAt(18)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    hour = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(17)\n    if (code1 < 48 || code1 > 50) {\n      return undefined // Not a digit between 0 and 2\n    }\n    const code2 = date.charCodeAt(18)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    if (code1 === 50 && code2 > 51) {\n      return undefined // Hour cannot be greater than 23\n    }\n    hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let minute = 0\n  if (date[20] === '0') {\n    const code = date.charCodeAt(21)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    minute = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(20)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(21)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let second = 0\n  if (date[23] === '0') {\n    const code = date.charCodeAt(24)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    second = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(23)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(24)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))\n  return result.getUTCDay() === weekday ? result : undefined\n}\n\n/**\n * @see https://httpwg.org/specs/rfc9110.html#obsolete.date.formats\n *\n * @param {string} date\n * @returns {Date | undefined}\n */\nfunction parseAscTimeDate (date) {\n  // This is assumed to be in UTC\n\n  if (\n    date.length !== 24 ||\n    date[7] !== ' ' ||\n    date[10] !== ' ' ||\n    date[19] !== ' '\n  ) {\n    return undefined\n  }\n\n  let weekday = -1\n  if (date[0] === 'S' && date[1] === 'u' && date[2] === 'n') { // Sunday\n    weekday = 0\n  } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n') { // Monday\n    weekday = 1\n  } else if (date[0] === 'T' && date[1] === 'u' && date[2] === 'e') { // Tuesday\n    weekday = 2\n  } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd') { // Wednesday\n    weekday = 3\n  } else if (date[0] === 'T' && date[1] === 'h' && date[2] === 'u') { // Thursday\n    weekday = 4\n  } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i') { // Friday\n    weekday = 5\n  } else if (date[0] === 'S' && date[1] === 'a' && date[2] === 't') { // Saturday\n    weekday = 6\n  } else {\n    return undefined // Not a valid day of the week\n  }\n\n  let monthIdx = -1\n  if (\n    (date[4] === 'J' && date[5] === 'a' && date[6] === 'n')\n  ) {\n    monthIdx = 0 // Jan\n  } else if (\n    (date[4] === 'F' && date[5] === 'e' && date[6] === 'b')\n  ) {\n    monthIdx = 1 // Feb\n  } else if (\n    (date[4] === 'M' && date[5] === 'a')\n  ) {\n    if (date[6] === 'r') {\n      monthIdx = 2 // Mar\n    } else if (date[6] === 'y') {\n      monthIdx = 4 // May\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[4] === 'J')\n  ) {\n    if (date[5] === 'a' && date[6] === 'n') {\n      monthIdx = 0 // Jan\n    } else if (date[5] === 'u') {\n      if (date[6] === 'n') {\n        monthIdx = 5 // Jun\n      } else if (date[6] === 'l') {\n        monthIdx = 6 // Jul\n      } else {\n        return undefined // Invalid month\n      }\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[4] === 'A')\n  ) {\n    if (date[5] === 'p' && date[6] === 'r') {\n      monthIdx = 3 // Apr\n    } else if (date[5] === 'u' && date[6] === 'g') {\n      monthIdx = 7 // Aug\n    } else {\n      return undefined // Invalid month\n    }\n  } else if (\n    (date[4] === 'S' && date[5] === 'e' && date[6] === 'p')\n  ) {\n    monthIdx = 8 // Sep\n  } else if (\n    (date[4] === 'O' && date[5] === 'c' && date[6] === 't')\n  ) {\n    monthIdx = 9 // Oct\n  } else if (\n    (date[4] === 'N' && date[5] === 'o' && date[6] === 'v')\n  ) {\n    monthIdx = 10 // Nov\n  } else if (\n    (date[4] === 'D' && date[5] === 'e' && date[6] === 'c')\n  ) {\n    monthIdx = 11 // Dec\n  } else {\n    // Not a valid month\n    return undefined\n  }\n\n  let day = 0\n  if (date[8] === ' ') {\n    // Single digit day, e.g. \"Sun Nov 6 08:49:37 1994\"\n    const code = date.charCodeAt(9)\n    if (code < 49 || code > 57) {\n      return undefined // Not a digit\n    }\n    day = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(8)\n    if (code1 < 49 || code1 > 51) {\n      return undefined // Not a digit between 1 and 3\n    }\n    const code2 = date.charCodeAt(9)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let hour = 0\n  if (date[11] === '0') {\n    const code = date.charCodeAt(12)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    hour = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(11)\n    if (code1 < 48 || code1 > 50) {\n      return undefined // Not a digit between 0 and 2\n    }\n    const code2 = date.charCodeAt(12)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    if (code1 === 50 && code2 > 51) {\n      return undefined // Hour cannot be greater than 23\n    }\n    hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let minute = 0\n  if (date[14] === '0') {\n    const code = date.charCodeAt(15)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    minute = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(14)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(15)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let second = 0\n  if (date[17] === '0') {\n    const code = date.charCodeAt(18)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    second = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(17)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(18)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  const yearDigit1 = date.charCodeAt(20)\n  if (yearDigit1 < 48 || yearDigit1 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit2 = date.charCodeAt(21)\n  if (yearDigit2 < 48 || yearDigit2 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit3 = date.charCodeAt(22)\n  if (yearDigit3 < 48 || yearDigit3 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit4 = date.charCodeAt(23)\n  if (yearDigit4 < 48 || yearDigit4 > 57) {\n    return undefined // Not a digit\n  }\n  const year = (yearDigit1 - 48) * 1000 + (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48)\n\n  const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))\n  return result.getUTCDay() === weekday ? result : undefined\n}\n\n/**\n * @see https://httpwg.org/specs/rfc9110.html#obsolete.date.formats\n *\n * @param {string} date\n * @returns {Date | undefined}\n */\nfunction parseRfc850Date (date) {\n  let commaIndex = -1\n\n  let weekday = -1\n  if (date[0] === 'S') {\n    if (date[1] === 'u' && date[2] === 'n' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {\n      weekday = 0 // Sunday\n      commaIndex = 6\n    } else if (date[1] === 'a' && date[2] === 't' && date[3] === 'u' && date[4] === 'r' && date[5] === 'd' && date[6] === 'a' && date[7] === 'y') {\n      weekday = 6 // Saturday\n      commaIndex = 8\n    }\n  } else if (date[0] === 'M' && date[1] === 'o' && date[2] === 'n' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {\n    weekday = 1 // Monday\n    commaIndex = 6\n  } else if (date[0] === 'T') {\n    if (date[1] === 'u' && date[2] === 'e' && date[3] === 's' && date[4] === 'd' && date[5] === 'a' && date[6] === 'y') {\n      weekday = 2 // Tuesday\n      commaIndex = 7\n    } else if (date[1] === 'h' && date[2] === 'u' && date[3] === 'r' && date[4] === 's' && date[5] === 'd' && date[6] === 'a' && date[7] === 'y') {\n      weekday = 4 // Thursday\n      commaIndex = 8\n    }\n  } else if (date[0] === 'W' && date[1] === 'e' && date[2] === 'd' && date[3] === 'n' && date[4] === 'e' && date[5] === 's' && date[6] === 'd' && date[7] === 'a' && date[8] === 'y') {\n    weekday = 3 // Wednesday\n    commaIndex = 9\n  } else if (date[0] === 'F' && date[1] === 'r' && date[2] === 'i' && date[3] === 'd' && date[4] === 'a' && date[5] === 'y') {\n    weekday = 5 // Friday\n    commaIndex = 6\n  } else {\n    // Not a valid day name\n    return undefined\n  }\n\n  if (\n    date[commaIndex] !== ',' ||\n    (date.length - commaIndex - 1) !== 23 ||\n    date[commaIndex + 1] !== ' ' ||\n    date[commaIndex + 4] !== '-' ||\n    date[commaIndex + 8] !== '-' ||\n    date[commaIndex + 11] !== ' ' ||\n    date[commaIndex + 14] !== ':' ||\n    date[commaIndex + 17] !== ':' ||\n    date[commaIndex + 20] !== ' ' ||\n    date[commaIndex + 21] !== 'G' ||\n    date[commaIndex + 22] !== 'M' ||\n    date[commaIndex + 23] !== 'T'\n  ) {\n    return undefined\n  }\n\n  let day = 0\n  if (date[commaIndex + 2] === '0') {\n    // Single digit day, e.g. \"Sun Nov 6 08:49:37 1994\"\n    const code = date.charCodeAt(commaIndex + 3)\n    if (code < 49 || code > 57) {\n      return undefined // Not a digit\n    }\n    day = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(commaIndex + 2)\n    if (code1 < 49 || code1 > 51) {\n      return undefined // Not a digit between 1 and 3\n    }\n    const code2 = date.charCodeAt(commaIndex + 3)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    day = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let monthIdx = -1\n  if (\n    (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'n')\n  ) {\n    monthIdx = 0 // Jan\n  } else if (\n    (date[commaIndex + 5] === 'F' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'b')\n  ) {\n    monthIdx = 1 // Feb\n  } else if (\n    (date[commaIndex + 5] === 'M' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'r')\n  ) {\n    monthIdx = 2 // Mar\n  } else if (\n    (date[commaIndex + 5] === 'A' && date[commaIndex + 6] === 'p' && date[commaIndex + 7] === 'r')\n  ) {\n    monthIdx = 3 // Apr\n  } else if (\n    (date[commaIndex + 5] === 'M' && date[commaIndex + 6] === 'a' && date[commaIndex + 7] === 'y')\n  ) {\n    monthIdx = 4 // May\n  } else if (\n    (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'n')\n  ) {\n    monthIdx = 5 // Jun\n  } else if (\n    (date[commaIndex + 5] === 'J' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'l')\n  ) {\n    monthIdx = 6 // Jul\n  } else if (\n    (date[commaIndex + 5] === 'A' && date[commaIndex + 6] === 'u' && date[commaIndex + 7] === 'g')\n  ) {\n    monthIdx = 7 // Aug\n  } else if (\n    (date[commaIndex + 5] === 'S' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'p')\n  ) {\n    monthIdx = 8 // Sep\n  } else if (\n    (date[commaIndex + 5] === 'O' && date[commaIndex + 6] === 'c' && date[commaIndex + 7] === 't')\n  ) {\n    monthIdx = 9 // Oct\n  } else if (\n    (date[commaIndex + 5] === 'N' && date[commaIndex + 6] === 'o' && date[commaIndex + 7] === 'v')\n  ) {\n    monthIdx = 10 // Nov\n  } else if (\n    (date[commaIndex + 5] === 'D' && date[commaIndex + 6] === 'e' && date[commaIndex + 7] === 'c')\n  ) {\n    monthIdx = 11 // Dec\n  } else {\n    // Not a valid month\n    return undefined\n  }\n\n  const yearDigit1 = date.charCodeAt(commaIndex + 9)\n  if (yearDigit1 < 48 || yearDigit1 > 57) {\n    return undefined // Not a digit\n  }\n  const yearDigit2 = date.charCodeAt(commaIndex + 10)\n  if (yearDigit2 < 48 || yearDigit2 > 57) {\n    return undefined // Not a digit\n  }\n\n  let year = (yearDigit1 - 48) * 10 + (yearDigit2 - 48) // Convert ASCII codes to number\n\n  // RFC 6265 states that the year is in the range 1970-2069.\n  // @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1\n  //\n  // 3. If the year-value is greater than or equal to 70 and less than or\n  //    equal to 99, increment the year-value by 1900.\n  // 4. If the year-value is greater than or equal to 0 and less than or\n  //    equal to 69, increment the year-value by 2000.\n  year += year < 70 ? 2000 : 1900\n\n  let hour = 0\n  if (date[commaIndex + 12] === '0') {\n    const code = date.charCodeAt(commaIndex + 13)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    hour = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(commaIndex + 12)\n    if (code1 < 48 || code1 > 50) {\n      return undefined // Not a digit between 0 and 2\n    }\n    const code2 = date.charCodeAt(commaIndex + 13)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    if (code1 === 50 && code2 > 51) {\n      return undefined // Hour cannot be greater than 23\n    }\n    hour = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let minute = 0\n  if (date[commaIndex + 15] === '0') {\n    const code = date.charCodeAt(commaIndex + 16)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    minute = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(commaIndex + 15)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(commaIndex + 16)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    minute = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  let second = 0\n  if (date[commaIndex + 18] === '0') {\n    const code = date.charCodeAt(commaIndex + 19)\n    if (code < 48 || code > 57) {\n      return undefined // Not a digit\n    }\n    second = code - 48 // Convert ASCII code to number\n  } else {\n    const code1 = date.charCodeAt(commaIndex + 18)\n    if (code1 < 48 || code1 > 53) {\n      return undefined // Not a digit between 0 and 5\n    }\n    const code2 = date.charCodeAt(commaIndex + 19)\n    if (code2 < 48 || code2 > 57) {\n      return undefined // Not a digit\n    }\n    second = (code1 - 48) * 10 + (code2 - 48) // Convert ASCII codes to number\n  }\n\n  const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second))\n  return result.getUTCDay() === weekday ? result : undefined\n}\n\nmodule.exports = {\n  parseHttpDate\n}\n"
  },
  {
    "path": "lib/util/promise.js",
    "content": "'use strict'\n\n/**\n * @template {*} T\n * @typedef {Object} DeferredPromise\n * @property {Promise<T>} promise\n * @property {(value?: T) => void} resolve\n * @property {(reason?: any) => void} reject\n */\n\n/**\n * @template {*} T\n * @returns {DeferredPromise<T>} An object containing a promise and its resolve/reject methods.\n */\nfunction createDeferredPromise () {\n  let res\n  let rej\n  const promise = new Promise((resolve, reject) => {\n    res = resolve\n    rej = reject\n  })\n\n  return { promise, resolve: res, reject: rej }\n}\n\nmodule.exports = {\n  createDeferredPromise\n}\n"
  },
  {
    "path": "lib/util/runtime-features.js",
    "content": "'use strict'\n\n/** @typedef {`node:${string}`} NodeModuleName */\n\n/** @type {Record<NodeModuleName, () => any>} */\nconst lazyLoaders = {\n  __proto__: null,\n  'node:crypto': () => require('node:crypto'),\n  'node:sqlite': () => require('node:sqlite'),\n  'node:worker_threads': () => require('node:worker_threads'),\n  'node:zlib': () => require('node:zlib')\n}\n\n/**\n * @param {NodeModuleName} moduleName\n * @returns {boolean}\n */\nfunction detectRuntimeFeatureByNodeModule (moduleName) {\n  try {\n    lazyLoaders[moduleName]()\n    return true\n  } catch (err) {\n    if (err.code !== 'ERR_UNKNOWN_BUILTIN_MODULE' && err.code !== 'ERR_NO_CRYPTO') {\n      throw err\n    }\n    return false\n  }\n}\n\n/**\n * @param {NodeModuleName} moduleName\n * @param {string} property\n * @returns {boolean}\n */\nfunction detectRuntimeFeatureByExportedProperty (moduleName, property) {\n  const module = lazyLoaders[moduleName]()\n  return typeof module[property] !== 'undefined'\n}\n\nconst runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd'])\n\n/** @type {Record<RuntimeFeatureByExportedProperty, [NodeModuleName, string]>} */\nconst exportedPropertyLookup = {\n  markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'],\n  zstd: ['node:zlib', 'createZstdDecompress']\n}\n\n/** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */\n\nconst runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite'])\n/** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */\n\nconst features = /** @type {const} */ ([\n  ...runtimeFeaturesAsNodeModule,\n  ...runtimeFeaturesByExportedProperty\n])\n\n/** @typedef {typeof features[number]} Feature */\n\n/**\n * @param {Feature} feature\n * @returns {boolean}\n */\nfunction detectRuntimeFeature (feature) {\n  if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) {\n    return detectRuntimeFeatureByNodeModule(`node:${feature}`)\n  } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) {\n    const [moduleName, property] = exportedPropertyLookup[feature]\n    return detectRuntimeFeatureByExportedProperty(moduleName, property)\n  }\n  throw new TypeError(`unknown feature: ${feature}`)\n}\n\n/**\n * @class\n * @name RuntimeFeatures\n */\nclass RuntimeFeatures {\n  /** @type {Map<Feature, boolean>} */\n  #map = new Map()\n\n  /**\n   * Clears all cached feature detections.\n   */\n  clear () {\n    this.#map.clear()\n  }\n\n  /**\n   * @param {Feature} feature\n   * @returns {boolean}\n   */\n  has (feature) {\n    return (\n      this.#map.get(feature) ?? this.#detectRuntimeFeature(feature)\n    )\n  }\n\n  /**\n   * @param {Feature} feature\n   * @param {boolean} value\n   */\n  set (feature, value) {\n    if (features.includes(feature) === false) {\n      throw new TypeError(`unknown feature: ${feature}`)\n    }\n    this.#map.set(feature, value)\n  }\n\n  /**\n   * @param {Feature} feature\n   * @returns {boolean}\n   */\n  #detectRuntimeFeature (feature) {\n    const result = detectRuntimeFeature(feature)\n    this.#map.set(feature, result)\n    return result\n  }\n}\n\nconst instance = new RuntimeFeatures()\n\nmodule.exports.runtimeFeatures = instance\nmodule.exports.default = instance\n"
  },
  {
    "path": "lib/util/stats.js",
    "content": "'use strict'\n\nconst {\n  kConnected,\n  kPending,\n  kRunning,\n  kSize,\n  kFree,\n  kQueued\n} = require('../core/symbols')\n\nclass ClientStats {\n  constructor (client) {\n    this.connected = client[kConnected]\n    this.pending = client[kPending]\n    this.running = client[kRunning]\n    this.size = client[kSize]\n  }\n}\n\nclass PoolStats {\n  constructor (pool) {\n    this.connected = pool[kConnected]\n    this.free = pool[kFree]\n    this.pending = pool[kPending]\n    this.queued = pool[kQueued]\n    this.running = pool[kRunning]\n    this.size = pool[kSize]\n  }\n}\n\nmodule.exports = { ClientStats, PoolStats }\n"
  },
  {
    "path": "lib/util/timers.js",
    "content": "'use strict'\n\n/**\n * This module offers an optimized timer implementation designed for scenarios\n * where high precision is not critical.\n *\n * The timer achieves faster performance by using a low-resolution approach,\n * with an accuracy target of within 500ms. This makes it particularly useful\n * for timers with delays of 1 second or more, where exact timing is less\n * crucial.\n *\n * It's important to note that Node.js timers are inherently imprecise, as\n * delays can occur due to the event loop being blocked by other operations.\n * Consequently, timers may trigger later than their scheduled time.\n */\n\n/**\n * The fastNow variable contains the internal fast timer clock value.\n *\n * @type {number}\n */\nlet fastNow = 0\n\n/**\n * RESOLUTION_MS represents the target resolution time in milliseconds.\n *\n * @type {number}\n * @default 1000\n */\nconst RESOLUTION_MS = 1e3\n\n/**\n * TICK_MS defines the desired interval in milliseconds between each tick.\n * The target value is set to half the resolution time, minus 1 ms, to account\n * for potential event loop overhead.\n *\n * @type {number}\n * @default 499\n */\nconst TICK_MS = (RESOLUTION_MS >> 1) - 1\n\n/**\n * fastNowTimeout is a Node.js timer used to manage and process\n * the FastTimers stored in the `fastTimers` array.\n *\n * @type {NodeJS.Timeout}\n */\nlet fastNowTimeout\n\n/**\n * The kFastTimer symbol is used to identify FastTimer instances.\n *\n * @type {Symbol}\n */\nconst kFastTimer = Symbol('kFastTimer')\n\n/**\n * The fastTimers array contains all active FastTimers.\n *\n * @type {FastTimer[]}\n */\nconst fastTimers = []\n\n/**\n * These constants represent the various states of a FastTimer.\n */\n\n/**\n * The `NOT_IN_LIST` constant indicates that the FastTimer is not included\n * in the `fastTimers` array. Timers with this status will not be processed\n * during the next tick by the `onTick` function.\n *\n * A FastTimer can be re-added to the `fastTimers` array by invoking the\n * `refresh` method on the FastTimer instance.\n *\n * @type {-2}\n */\nconst NOT_IN_LIST = -2\n\n/**\n * The `TO_BE_CLEARED` constant indicates that the FastTimer is scheduled\n * for removal from the `fastTimers` array. A FastTimer in this state will\n * be removed in the next tick by the `onTick` function and will no longer\n * be processed.\n *\n * This status is also set when the `clear` method is called on the FastTimer instance.\n *\n * @type {-1}\n */\nconst TO_BE_CLEARED = -1\n\n/**\n * The `PENDING` constant signifies that the FastTimer is awaiting processing\n * in the next tick by the `onTick` function. Timers with this status will have\n * their `_idleStart` value set and their status updated to `ACTIVE` in the next tick.\n *\n * @type {0}\n */\nconst PENDING = 0\n\n/**\n * The `ACTIVE` constant indicates that the FastTimer is active and waiting\n * for its timer to expire. During the next tick, the `onTick` function will\n * check if the timer has expired, and if so, it will execute the associated callback.\n *\n * @type {1}\n */\nconst ACTIVE = 1\n\n/**\n * The onTick function processes the fastTimers array.\n *\n * @returns {void}\n */\nfunction onTick () {\n  /**\n   * Increment the fastNow value by the TICK_MS value, despite the actual time\n   * that has passed since the last tick. This approach ensures independence\n   * from the system clock and delays caused by a blocked event loop.\n   *\n   * @type {number}\n   */\n  fastNow += TICK_MS\n\n  /**\n   * The `idx` variable is used to iterate over the `fastTimers` array.\n   * Expired timers are removed by replacing them with the last element in the array.\n   * Consequently, `idx` is only incremented when the current element is not removed.\n   *\n   * @type {number}\n   */\n  let idx = 0\n\n  /**\n   * The len variable will contain the length of the fastTimers array\n   * and will be decremented when a FastTimer should be removed from the\n   * fastTimers array.\n   *\n   * @type {number}\n   */\n  let len = fastTimers.length\n\n  while (idx < len) {\n    /**\n     * @type {FastTimer}\n     */\n    const timer = fastTimers[idx]\n\n    // If the timer is in the ACTIVE state and the timer has expired, it will\n    // be processed in the next tick.\n    if (timer._state === PENDING) {\n      // Set the _idleStart value to the fastNow value minus the TICK_MS value\n      // to account for the time the timer was in the PENDING state.\n      timer._idleStart = fastNow - TICK_MS\n      timer._state = ACTIVE\n    } else if (\n      timer._state === ACTIVE &&\n      fastNow >= timer._idleStart + timer._idleTimeout\n    ) {\n      timer._state = TO_BE_CLEARED\n      timer._idleStart = -1\n      timer._onTimeout(timer._timerArg)\n    }\n\n    if (timer._state === TO_BE_CLEARED) {\n      timer._state = NOT_IN_LIST\n\n      // Move the last element to the current index and decrement len if it is\n      // not the only element in the array.\n      if (--len !== 0) {\n        fastTimers[idx] = fastTimers[len]\n      }\n    } else {\n      ++idx\n    }\n  }\n\n  // Set the length of the fastTimers array to the new length and thus\n  // removing the excess FastTimers elements from the array.\n  fastTimers.length = len\n\n  // If there are still active FastTimers in the array, refresh the Timer.\n  // If there are no active FastTimers, the timer will be refreshed again\n  // when a new FastTimer is instantiated.\n  if (fastTimers.length !== 0) {\n    refreshTimeout()\n  }\n}\n\nfunction refreshTimeout () {\n  // If the fastNowTimeout is already set and the Timer has the refresh()-\n  // method available, call it to refresh the timer.\n  // Some timer objects returned by setTimeout may not have a .refresh()\n  // method (e.g. mocked timers in tests).\n  if (fastNowTimeout?.refresh) {\n    fastNowTimeout.refresh()\n    // fastNowTimeout is not instantiated yet or refresh is not availabe,\n    // create a new Timer.\n  } else {\n    clearTimeout(fastNowTimeout)\n    fastNowTimeout = setTimeout(onTick, TICK_MS)\n    // If the Timer has an unref method, call it to allow the process to exit,\n    // if there are no other active handles. When using fake timers or mocked\n    // environments (like Jest), .unref() may not be defined,\n    fastNowTimeout?.unref()\n  }\n}\n\n/**\n * The `FastTimer` class is a data structure designed to store and manage\n * timer information.\n */\nclass FastTimer {\n  [kFastTimer] = true\n\n  /**\n   * The state of the timer, which can be one of the following:\n   * - NOT_IN_LIST (-2)\n   * - TO_BE_CLEARED (-1)\n   * - PENDING (0)\n   * - ACTIVE (1)\n   *\n   * @type {-2|-1|0|1}\n   * @private\n   */\n  _state = NOT_IN_LIST\n\n  /**\n   * The number of milliseconds to wait before calling the callback.\n   *\n   * @type {number}\n   * @private\n   */\n  _idleTimeout = -1\n\n  /**\n   * The time in milliseconds when the timer was started. This value is used to\n   * calculate when the timer should expire.\n   *\n   * @type {number}\n   * @default -1\n   * @private\n   */\n  _idleStart = -1\n\n  /**\n   * The function to be executed when the timer expires.\n   * @type {Function}\n   * @private\n   */\n  _onTimeout\n\n  /**\n   * The argument to be passed to the callback when the timer expires.\n   *\n   * @type {*}\n   * @private\n   */\n  _timerArg\n\n  /**\n   * @constructor\n   * @param {Function} callback A function to be executed after the timer\n   * expires.\n   * @param {number} delay The time, in milliseconds that the timer should wait\n   * before the specified function or code is executed.\n   * @param {*} arg\n   */\n  constructor (callback, delay, arg) {\n    this._onTimeout = callback\n    this._idleTimeout = delay\n    this._timerArg = arg\n\n    this.refresh()\n  }\n\n  /**\n   * Sets the timer's start time to the current time, and reschedules the timer\n   * to call its callback at the previously specified duration adjusted to the\n   * current time.\n   * Using this on a timer that has already called its callback will reactivate\n   * the timer.\n   *\n   * @returns {void}\n   */\n  refresh () {\n    // In the special case that the timer is not in the list of active timers,\n    // add it back to the array to be processed in the next tick by the onTick\n    // function.\n    if (this._state === NOT_IN_LIST) {\n      fastTimers.push(this)\n    }\n\n    // If the timer is the only active timer, refresh the fastNowTimeout for\n    // better resolution.\n    if (!fastNowTimeout || fastTimers.length === 1) {\n      refreshTimeout()\n    }\n\n    // Setting the state to PENDING will cause the timer to be reset in the\n    // next tick by the onTick function.\n    this._state = PENDING\n  }\n\n  /**\n   * The `clear` method cancels the timer, preventing it from executing.\n   *\n   * @returns {void}\n   * @private\n   */\n  clear () {\n    // Set the state to TO_BE_CLEARED to mark the timer for removal in the next\n    // tick by the onTick function.\n    this._state = TO_BE_CLEARED\n\n    // Reset the _idleStart value to -1 to indicate that the timer is no longer\n    // active.\n    this._idleStart = -1\n  }\n}\n\n/**\n * This module exports a setTimeout and clearTimeout function that can be\n * used as a drop-in replacement for the native functions.\n */\nmodule.exports = {\n  /**\n   * The setTimeout() method sets a timer which executes a function once the\n   * timer expires.\n   * @param {Function} callback A function to be executed after the timer\n   * expires.\n   * @param {number} delay The time, in milliseconds that the timer should\n   * wait before the specified function or code is executed.\n   * @param {*} [arg] An optional argument to be passed to the callback function\n   * when the timer expires.\n   * @returns {NodeJS.Timeout|FastTimer}\n   */\n  setTimeout (callback, delay, arg) {\n    // If the delay is less than or equal to the RESOLUTION_MS value return a\n    // native Node.js Timer instance.\n    return delay <= RESOLUTION_MS\n      ? setTimeout(callback, delay, arg)\n      : new FastTimer(callback, delay, arg)\n  },\n  /**\n   * The clearTimeout method cancels an instantiated Timer previously created\n   * by calling setTimeout.\n   *\n   * @param {NodeJS.Timeout|FastTimer} timeout\n   */\n  clearTimeout (timeout) {\n    // If the timeout is a FastTimer, call its own clear method.\n    if (timeout[kFastTimer]) {\n      /**\n       * @type {FastTimer}\n       */\n      timeout.clear()\n      // Otherwise it is an instance of a native NodeJS.Timeout, so call the\n      // Node.js native clearTimeout function.\n    } else {\n      clearTimeout(timeout)\n    }\n  },\n  /**\n   * The setFastTimeout() method sets a fastTimer which executes a function once\n   * the timer expires.\n   * @param {Function} callback A function to be executed after the timer\n   * expires.\n   * @param {number} delay The time, in milliseconds that the timer should\n   * wait before the specified function or code is executed.\n   * @param {*} [arg] An optional argument to be passed to the callback function\n   * when the timer expires.\n   * @returns {FastTimer}\n   */\n  setFastTimeout (callback, delay, arg) {\n    return new FastTimer(callback, delay, arg)\n  },\n  /**\n   * The clearTimeout method cancels an instantiated FastTimer previously\n   * created by calling setFastTimeout.\n   *\n   * @param {FastTimer} timeout\n   */\n  clearFastTimeout (timeout) {\n    timeout.clear()\n  },\n  /**\n   * The now method returns the value of the internal fast timer clock.\n   *\n   * @returns {number}\n   */\n  now () {\n    return fastNow\n  },\n  /**\n   * Trigger the onTick function to process the fastTimers array.\n   * Exported for testing purposes only.\n   * Marking as deprecated to discourage any use outside of testing.\n   * @deprecated\n   * @param {number} [delay=0] The delay in milliseconds to add to the now value.\n   */\n  tick (delay = 0) {\n    fastNow += delay - RESOLUTION_MS + 1\n    onTick()\n    onTick()\n  },\n  /**\n   * Reset FastTimers.\n   * Exported for testing purposes only.\n   * Marking as deprecated to discourage any use outside of testing.\n   * @deprecated\n   */\n  reset () {\n    fastNow = 0\n    fastTimers.length = 0\n    clearTimeout(fastNowTimeout)\n    fastNowTimeout = null\n  },\n  /**\n   * Exporting for testing purposes only.\n   * Marking as deprecated to discourage any use outside of testing.\n   * @deprecated\n   */\n  kFastTimer\n}\n"
  },
  {
    "path": "lib/web/cache/cache.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\n\nconst { kConstruct } = require('../../core/symbols')\nconst { urlEquals, getFieldValues } = require('./util')\nconst { kEnumerableProperty, isDisturbed } = require('../../core/util')\nconst { webidl } = require('../webidl')\nconst { cloneResponse, fromInnerResponse, getResponseState } = require('../fetch/response')\nconst { Request, fromInnerRequest, getRequestState } = require('../fetch/request')\nconst { fetching } = require('../fetch/index')\nconst { urlIsHttpHttpsScheme, readAllBytes } = require('../fetch/util')\nconst { createDeferredPromise } = require('../../util/promise')\n\n/**\n * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation\n * @typedef {Object} CacheBatchOperation\n * @property {'delete' | 'put'} type\n * @property {any} request\n * @property {any} response\n * @property {import('../../../types/cache').CacheQueryOptions} options\n */\n\n/**\n * @see https://w3c.github.io/ServiceWorker/#dfn-request-response-list\n * @typedef {[any, any][]} requestResponseList\n */\n\nclass Cache {\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-request-response-list\n   * @type {requestResponseList}\n   */\n  #relevantRequestResponseList\n\n  constructor () {\n    if (arguments[0] !== kConstruct) {\n      webidl.illegalConstructor()\n    }\n\n    webidl.util.markAsUncloneable(this)\n    this.#relevantRequestResponseList = arguments[1]\n  }\n\n  async match (request, options = {}) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.match'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    request = webidl.converters.RequestInfo(request)\n    options = webidl.converters.CacheQueryOptions(options, prefix, 'options')\n\n    const p = this.#internalMatchAll(request, options, 1)\n\n    if (p.length === 0) {\n      return\n    }\n\n    return p[0]\n  }\n\n  async matchAll (request = undefined, options = {}) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.matchAll'\n    if (request !== undefined) request = webidl.converters.RequestInfo(request)\n    options = webidl.converters.CacheQueryOptions(options, prefix, 'options')\n\n    return this.#internalMatchAll(request, options)\n  }\n\n  async add (request) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.add'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    request = webidl.converters.RequestInfo(request)\n\n    // 1.\n    const requests = [request]\n\n    // 2.\n    const responseArrayPromise = this.addAll(requests)\n\n    // 3.\n    return await responseArrayPromise\n  }\n\n  async addAll (requests) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.addAll'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    // 1.\n    const responsePromises = []\n\n    // 2.\n    const requestList = []\n\n    // 3.\n    for (let request of requests) {\n      if (request === undefined) {\n        throw webidl.errors.conversionFailed({\n          prefix,\n          argument: 'Argument 1',\n          types: ['undefined is not allowed']\n        })\n      }\n\n      request = webidl.converters.RequestInfo(request)\n\n      if (typeof request === 'string') {\n        continue\n      }\n\n      // 3.1\n      const r = getRequestState(request)\n\n      // 3.2\n      if (!urlIsHttpHttpsScheme(r.url) || r.method !== 'GET') {\n        throw webidl.errors.exception({\n          header: prefix,\n          message: 'Expected http/s scheme when method is not GET.'\n        })\n      }\n    }\n\n    // 4.\n    /** @type {ReturnType<typeof fetching>[]} */\n    const fetchControllers = []\n\n    // 5.\n    for (const request of requests) {\n      // 5.1\n      const r = getRequestState(new Request(request))\n\n      // 5.2\n      if (!urlIsHttpHttpsScheme(r.url)) {\n        throw webidl.errors.exception({\n          header: prefix,\n          message: 'Expected http/s scheme.'\n        })\n      }\n\n      // 5.4\n      r.initiator = 'fetch'\n      r.destination = 'subresource'\n\n      // 5.5\n      requestList.push(r)\n\n      // 5.6\n      const responsePromise = createDeferredPromise()\n\n      // 5.7\n      fetchControllers.push(fetching({\n        request: r,\n        processResponse (response) {\n          // 1.\n          if (response.type === 'error' || response.status === 206 || response.status < 200 || response.status > 299) {\n            responsePromise.reject(webidl.errors.exception({\n              header: 'Cache.addAll',\n              message: 'Received an invalid status code or the request failed.'\n            }))\n          } else if (response.headersList.contains('vary')) { // 2.\n            // 2.1\n            const fieldValues = getFieldValues(response.headersList.get('vary'))\n\n            // 2.2\n            for (const fieldValue of fieldValues) {\n              // 2.2.1\n              if (fieldValue === '*') {\n                responsePromise.reject(webidl.errors.exception({\n                  header: 'Cache.addAll',\n                  message: 'invalid vary field value'\n                }))\n\n                for (const controller of fetchControllers) {\n                  controller.abort()\n                }\n\n                return\n              }\n            }\n          }\n        },\n        processResponseEndOfBody (response) {\n          // 1.\n          if (response.aborted) {\n            responsePromise.reject(new DOMException('aborted', 'AbortError'))\n            return\n          }\n\n          // 2.\n          responsePromise.resolve(response)\n        }\n      }))\n\n      // 5.8\n      responsePromises.push(responsePromise.promise)\n    }\n\n    // 6.\n    const p = Promise.all(responsePromises)\n\n    // 7.\n    const responses = await p\n\n    // 7.1\n    const operations = []\n\n    // 7.2\n    let index = 0\n\n    // 7.3\n    for (const response of responses) {\n      // 7.3.1\n      /** @type {CacheBatchOperation} */\n      const operation = {\n        type: 'put', // 7.3.2\n        request: requestList[index], // 7.3.3\n        response // 7.3.4\n      }\n\n      operations.push(operation) // 7.3.5\n\n      index++ // 7.3.6\n    }\n\n    // 7.5\n    const cacheJobPromise = createDeferredPromise()\n\n    // 7.6.1\n    let errorData = null\n\n    // 7.6.2\n    try {\n      this.#batchCacheOperations(operations)\n    } catch (e) {\n      errorData = e\n    }\n\n    // 7.6.3\n    queueMicrotask(() => {\n      // 7.6.3.1\n      if (errorData === null) {\n        cacheJobPromise.resolve(undefined)\n      } else {\n        // 7.6.3.2\n        cacheJobPromise.reject(errorData)\n      }\n    })\n\n    // 7.7\n    return cacheJobPromise.promise\n  }\n\n  async put (request, response) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.put'\n    webidl.argumentLengthCheck(arguments, 2, prefix)\n\n    request = webidl.converters.RequestInfo(request)\n    response = webidl.converters.Response(response, prefix, 'response')\n\n    // 1.\n    let innerRequest = null\n\n    // 2.\n    if (webidl.is.Request(request)) {\n      innerRequest = getRequestState(request)\n    } else { // 3.\n      innerRequest = getRequestState(new Request(request))\n    }\n\n    // 4.\n    if (!urlIsHttpHttpsScheme(innerRequest.url) || innerRequest.method !== 'GET') {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: 'Expected an http/s scheme when method is not GET'\n      })\n    }\n\n    // 5.\n    const innerResponse = getResponseState(response)\n\n    // 6.\n    if (innerResponse.status === 206) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: 'Got 206 status'\n      })\n    }\n\n    // 7.\n    if (innerResponse.headersList.contains('vary')) {\n      // 7.1.\n      const fieldValues = getFieldValues(innerResponse.headersList.get('vary'))\n\n      // 7.2.\n      for (const fieldValue of fieldValues) {\n        // 7.2.1\n        if (fieldValue === '*') {\n          throw webidl.errors.exception({\n            header: prefix,\n            message: 'Got * vary field value'\n          })\n        }\n      }\n    }\n\n    // 8.\n    if (innerResponse.body && (isDisturbed(innerResponse.body.stream) || innerResponse.body.stream.locked)) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: 'Response body is locked or disturbed'\n      })\n    }\n\n    // 9.\n    const clonedResponse = cloneResponse(innerResponse)\n\n    // 10.\n    const bodyReadPromise = createDeferredPromise()\n\n    // 11.\n    if (innerResponse.body != null) {\n      // 11.1\n      const stream = innerResponse.body.stream\n\n      // 11.2\n      const reader = stream.getReader()\n\n      // 11.3\n      readAllBytes(reader, bodyReadPromise.resolve, bodyReadPromise.reject)\n    } else {\n      bodyReadPromise.resolve(undefined)\n    }\n\n    // 12.\n    /** @type {CacheBatchOperation[]} */\n    const operations = []\n\n    // 13.\n    /** @type {CacheBatchOperation} */\n    const operation = {\n      type: 'put', // 14.\n      request: innerRequest, // 15.\n      response: clonedResponse // 16.\n    }\n\n    // 17.\n    operations.push(operation)\n\n    // 19.\n    const bytes = await bodyReadPromise.promise\n\n    if (clonedResponse.body != null) {\n      clonedResponse.body.source = bytes\n    }\n\n    // 19.1\n    const cacheJobPromise = createDeferredPromise()\n\n    // 19.2.1\n    let errorData = null\n\n    // 19.2.2\n    try {\n      this.#batchCacheOperations(operations)\n    } catch (e) {\n      errorData = e\n    }\n\n    // 19.2.3\n    queueMicrotask(() => {\n      // 19.2.3.1\n      if (errorData === null) {\n        cacheJobPromise.resolve()\n      } else { // 19.2.3.2\n        cacheJobPromise.reject(errorData)\n      }\n    })\n\n    return cacheJobPromise.promise\n  }\n\n  async delete (request, options = {}) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.delete'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    request = webidl.converters.RequestInfo(request)\n    options = webidl.converters.CacheQueryOptions(options, prefix, 'options')\n\n    /**\n     * @type {Request}\n     */\n    let r = null\n\n    if (webidl.is.Request(request)) {\n      r = getRequestState(request)\n\n      if (r.method !== 'GET' && !options.ignoreMethod) {\n        return false\n      }\n    } else {\n      assert(typeof request === 'string')\n\n      r = getRequestState(new Request(request))\n    }\n\n    /** @type {CacheBatchOperation[]} */\n    const operations = []\n\n    /** @type {CacheBatchOperation} */\n    const operation = {\n      type: 'delete',\n      request: r,\n      options\n    }\n\n    operations.push(operation)\n\n    const cacheJobPromise = createDeferredPromise()\n\n    let errorData = null\n    let requestResponses\n\n    try {\n      requestResponses = this.#batchCacheOperations(operations)\n    } catch (e) {\n      errorData = e\n    }\n\n    queueMicrotask(() => {\n      if (errorData === null) {\n        cacheJobPromise.resolve(!!requestResponses?.length)\n      } else {\n        cacheJobPromise.reject(errorData)\n      }\n    })\n\n    return cacheJobPromise.promise\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#dom-cache-keys\n   * @param {any} request\n   * @param {import('../../../types/cache').CacheQueryOptions} options\n   * @returns {Promise<readonly Request[]>}\n   */\n  async keys (request = undefined, options = {}) {\n    webidl.brandCheck(this, Cache)\n\n    const prefix = 'Cache.keys'\n\n    if (request !== undefined) request = webidl.converters.RequestInfo(request)\n    options = webidl.converters.CacheQueryOptions(options, prefix, 'options')\n\n    // 1.\n    let r = null\n\n    // 2.\n    if (request !== undefined) {\n      // 2.1\n      if (webidl.is.Request(request)) {\n        // 2.1.1\n        r = getRequestState(request)\n\n        // 2.1.2\n        if (r.method !== 'GET' && !options.ignoreMethod) {\n          return []\n        }\n      } else if (typeof request === 'string') { // 2.2\n        r = getRequestState(new Request(request))\n      }\n    }\n\n    // 4.\n    const promise = createDeferredPromise()\n\n    // 5.\n    // 5.1\n    const requests = []\n\n    // 5.2\n    if (request === undefined) {\n      // 5.2.1\n      for (const requestResponse of this.#relevantRequestResponseList) {\n        // 5.2.1.1\n        requests.push(requestResponse[0])\n      }\n    } else { // 5.3\n      // 5.3.1\n      const requestResponses = this.#queryCache(r, options)\n\n      // 5.3.2\n      for (const requestResponse of requestResponses) {\n        // 5.3.2.1\n        requests.push(requestResponse[0])\n      }\n    }\n\n    // 5.4\n    queueMicrotask(() => {\n      // 5.4.1\n      const requestList = []\n\n      // 5.4.2\n      for (const request of requests) {\n        const requestObject = fromInnerRequest(\n          request,\n          undefined,\n          new AbortController().signal,\n          'immutable'\n        )\n        // 5.4.2.1\n        requestList.push(requestObject)\n      }\n\n      // 5.4.3\n      promise.resolve(Object.freeze(requestList))\n    })\n\n    return promise.promise\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#batch-cache-operations-algorithm\n   * @param {CacheBatchOperation[]} operations\n   * @returns {requestResponseList}\n   */\n  #batchCacheOperations (operations) {\n    // 1.\n    const cache = this.#relevantRequestResponseList\n\n    // 2.\n    const backupCache = [...cache]\n\n    // 3.\n    const addedItems = []\n\n    // 4.1\n    const resultList = []\n\n    try {\n      // 4.2\n      for (const operation of operations) {\n        // 4.2.1\n        if (operation.type !== 'delete' && operation.type !== 'put') {\n          throw webidl.errors.exception({\n            header: 'Cache.#batchCacheOperations',\n            message: 'operation type does not match \"delete\" or \"put\"'\n          })\n        }\n\n        // 4.2.2\n        if (operation.type === 'delete' && operation.response != null) {\n          throw webidl.errors.exception({\n            header: 'Cache.#batchCacheOperations',\n            message: 'delete operation should not have an associated response'\n          })\n        }\n\n        // 4.2.3\n        if (this.#queryCache(operation.request, operation.options, addedItems).length) {\n          throw new DOMException('???', 'InvalidStateError')\n        }\n\n        // 4.2.4\n        let requestResponses\n\n        // 4.2.5\n        if (operation.type === 'delete') {\n          // 4.2.5.1\n          requestResponses = this.#queryCache(operation.request, operation.options)\n\n          // TODO: the spec is wrong, this is needed to pass WPTs\n          if (requestResponses.length === 0) {\n            return []\n          }\n\n          // 4.2.5.2\n          for (const requestResponse of requestResponses) {\n            const idx = cache.indexOf(requestResponse)\n            assert(idx !== -1)\n\n            // 4.2.5.2.1\n            cache.splice(idx, 1)\n          }\n        } else if (operation.type === 'put') { // 4.2.6\n          // 4.2.6.1\n          if (operation.response == null) {\n            throw webidl.errors.exception({\n              header: 'Cache.#batchCacheOperations',\n              message: 'put operation should have an associated response'\n            })\n          }\n\n          // 4.2.6.2\n          const r = operation.request\n\n          // 4.2.6.3\n          if (!urlIsHttpHttpsScheme(r.url)) {\n            throw webidl.errors.exception({\n              header: 'Cache.#batchCacheOperations',\n              message: 'expected http or https scheme'\n            })\n          }\n\n          // 4.2.6.4\n          if (r.method !== 'GET') {\n            throw webidl.errors.exception({\n              header: 'Cache.#batchCacheOperations',\n              message: 'not get method'\n            })\n          }\n\n          // 4.2.6.5\n          if (operation.options != null) {\n            throw webidl.errors.exception({\n              header: 'Cache.#batchCacheOperations',\n              message: 'options must not be defined'\n            })\n          }\n\n          // 4.2.6.6\n          requestResponses = this.#queryCache(operation.request)\n\n          // 4.2.6.7\n          for (const requestResponse of requestResponses) {\n            const idx = cache.indexOf(requestResponse)\n            assert(idx !== -1)\n\n            // 4.2.6.7.1\n            cache.splice(idx, 1)\n          }\n\n          // 4.2.6.8\n          cache.push([operation.request, operation.response])\n\n          // 4.2.6.10\n          addedItems.push([operation.request, operation.response])\n        }\n\n        // 4.2.7\n        resultList.push([operation.request, operation.response])\n      }\n\n      // 4.3\n      return resultList\n    } catch (e) { // 5.\n      // 5.1\n      this.#relevantRequestResponseList.length = 0\n\n      // 5.2\n      this.#relevantRequestResponseList = backupCache\n\n      // 5.3\n      throw e\n    }\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#query-cache\n   * @param {any} requestQuery\n   * @param {import('../../../types/cache').CacheQueryOptions} options\n   * @param {requestResponseList} targetStorage\n   * @returns {requestResponseList}\n   */\n  #queryCache (requestQuery, options, targetStorage) {\n    /** @type {requestResponseList} */\n    const resultList = []\n\n    const storage = targetStorage ?? this.#relevantRequestResponseList\n\n    for (const requestResponse of storage) {\n      const [cachedRequest, cachedResponse] = requestResponse\n      if (this.#requestMatchesCachedItem(requestQuery, cachedRequest, cachedResponse, options)) {\n        resultList.push(requestResponse)\n      }\n    }\n\n    return resultList\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#request-matches-cached-item-algorithm\n   * @param {any} requestQuery\n   * @param {any} request\n   * @param {any | null} response\n   * @param {import('../../../types/cache').CacheQueryOptions | undefined} options\n   * @returns {boolean}\n   */\n  #requestMatchesCachedItem (requestQuery, request, response = null, options) {\n    // if (options?.ignoreMethod === false && request.method === 'GET') {\n    //   return false\n    // }\n\n    const queryURL = new URL(requestQuery.url)\n\n    const cachedURL = new URL(request.url)\n\n    if (options?.ignoreSearch) {\n      cachedURL.search = ''\n\n      queryURL.search = ''\n    }\n\n    if (!urlEquals(queryURL, cachedURL, true)) {\n      return false\n    }\n\n    if (\n      response == null ||\n      options?.ignoreVary ||\n      !response.headersList.contains('vary')\n    ) {\n      return true\n    }\n\n    const fieldValues = getFieldValues(response.headersList.get('vary'))\n\n    for (const fieldValue of fieldValues) {\n      if (fieldValue === '*') {\n        return false\n      }\n\n      const requestValue = request.headersList.get(fieldValue)\n      const queryValue = requestQuery.headersList.get(fieldValue)\n\n      // If one has the header and the other doesn't, or one has\n      // a different value than the other, return false\n      if (requestValue !== queryValue) {\n        return false\n      }\n    }\n\n    return true\n  }\n\n  #internalMatchAll (request, options, maxResponses = Infinity) {\n    // 1.\n    let r = null\n\n    // 2.\n    if (request !== undefined) {\n      if (webidl.is.Request(request)) {\n        // 2.1.1\n        r = getRequestState(request)\n\n        // 2.1.2\n        if (r.method !== 'GET' && !options.ignoreMethod) {\n          return []\n        }\n      } else if (typeof request === 'string') {\n        // 2.2.1\n        r = getRequestState(new Request(request))\n      }\n    }\n\n    // 5.\n    // 5.1\n    const responses = []\n\n    // 5.2\n    if (request === undefined) {\n      // 5.2.1\n      for (const requestResponse of this.#relevantRequestResponseList) {\n        responses.push(requestResponse[1])\n      }\n    } else { // 5.3\n      // 5.3.1\n      const requestResponses = this.#queryCache(r, options)\n\n      // 5.3.2\n      for (const requestResponse of requestResponses) {\n        responses.push(requestResponse[1])\n      }\n    }\n\n    // 5.4\n    // We don't implement CORs so we don't need to loop over the responses, yay!\n\n    // 5.5.1\n    const responseList = []\n\n    // 5.5.2\n    for (const response of responses) {\n      // 5.5.2.1\n      const responseObject = fromInnerResponse(cloneResponse(response), 'immutable')\n\n      responseList.push(responseObject)\n\n      if (responseList.length >= maxResponses) {\n        break\n      }\n    }\n\n    // 6.\n    return Object.freeze(responseList)\n  }\n}\n\nObject.defineProperties(Cache.prototype, {\n  [Symbol.toStringTag]: {\n    value: 'Cache',\n    configurable: true\n  },\n  match: kEnumerableProperty,\n  matchAll: kEnumerableProperty,\n  add: kEnumerableProperty,\n  addAll: kEnumerableProperty,\n  put: kEnumerableProperty,\n  delete: kEnumerableProperty,\n  keys: kEnumerableProperty\n})\n\nconst cacheQueryOptionConverters = [\n  {\n    key: 'ignoreSearch',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'ignoreMethod',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'ignoreVary',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  }\n]\n\nwebidl.converters.CacheQueryOptions = webidl.dictionaryConverter(cacheQueryOptionConverters)\n\nwebidl.converters.MultiCacheQueryOptions = webidl.dictionaryConverter([\n  ...cacheQueryOptionConverters,\n  {\n    key: 'cacheName',\n    converter: webidl.converters.DOMString\n  }\n])\n\nwebidl.converters.Response = webidl.interfaceConverter(\n  webidl.is.Response,\n  'Response'\n)\n\nwebidl.converters['sequence<RequestInfo>'] = webidl.sequenceConverter(\n  webidl.converters.RequestInfo\n)\n\nmodule.exports = {\n  Cache\n}\n"
  },
  {
    "path": "lib/web/cache/cachestorage.js",
    "content": "'use strict'\n\nconst { Cache } = require('./cache')\nconst { webidl } = require('../webidl')\nconst { kEnumerableProperty } = require('../../core/util')\nconst { kConstruct } = require('../../core/symbols')\n\nclass CacheStorage {\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-name-to-cache-map\n   * @type {Map<string, import('./cache').requestResponseList}\n   */\n  #caches = new Map()\n\n  constructor () {\n    if (arguments[0] !== kConstruct) {\n      webidl.illegalConstructor()\n    }\n\n    webidl.util.markAsUncloneable(this)\n  }\n\n  async match (request, options = {}) {\n    webidl.brandCheck(this, CacheStorage)\n    webidl.argumentLengthCheck(arguments, 1, 'CacheStorage.match')\n\n    request = webidl.converters.RequestInfo(request)\n    options = webidl.converters.MultiCacheQueryOptions(options)\n\n    // 1.\n    if (options.cacheName != null) {\n      // 1.1.1.1\n      if (this.#caches.has(options.cacheName)) {\n        // 1.1.1.1.1\n        const cacheList = this.#caches.get(options.cacheName)\n        const cache = new Cache(kConstruct, cacheList)\n\n        return await cache.match(request, options)\n      }\n    } else { // 2.\n      // 2.2\n      for (const cacheList of this.#caches.values()) {\n        const cache = new Cache(kConstruct, cacheList)\n\n        // 2.2.1.2\n        const response = await cache.match(request, options)\n\n        if (response !== undefined) {\n          return response\n        }\n      }\n    }\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#cache-storage-has\n   * @param {string} cacheName\n   * @returns {Promise<boolean>}\n   */\n  async has (cacheName) {\n    webidl.brandCheck(this, CacheStorage)\n\n    const prefix = 'CacheStorage.has'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')\n\n    // 2.1.1\n    // 2.2\n    return this.#caches.has(cacheName)\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#dom-cachestorage-open\n   * @param {string} cacheName\n   * @returns {Promise<Cache>}\n   */\n  async open (cacheName) {\n    webidl.brandCheck(this, CacheStorage)\n\n    const prefix = 'CacheStorage.open'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')\n\n    // 2.1\n    if (this.#caches.has(cacheName)) {\n      // await caches.open('v1') !== await caches.open('v1')\n\n      // 2.1.1\n      const cache = this.#caches.get(cacheName)\n\n      // 2.1.1.1\n      return new Cache(kConstruct, cache)\n    }\n\n    // 2.2\n    const cache = []\n\n    // 2.3\n    this.#caches.set(cacheName, cache)\n\n    // 2.4\n    return new Cache(kConstruct, cache)\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#cache-storage-delete\n   * @param {string} cacheName\n   * @returns {Promise<boolean>}\n   */\n  async delete (cacheName) {\n    webidl.brandCheck(this, CacheStorage)\n\n    const prefix = 'CacheStorage.delete'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    cacheName = webidl.converters.DOMString(cacheName, prefix, 'cacheName')\n\n    return this.#caches.delete(cacheName)\n  }\n\n  /**\n   * @see https://w3c.github.io/ServiceWorker/#cache-storage-keys\n   * @returns {Promise<string[]>}\n   */\n  async keys () {\n    webidl.brandCheck(this, CacheStorage)\n\n    // 2.1\n    const keys = this.#caches.keys()\n\n    // 2.2\n    return [...keys]\n  }\n}\n\nObject.defineProperties(CacheStorage.prototype, {\n  [Symbol.toStringTag]: {\n    value: 'CacheStorage',\n    configurable: true\n  },\n  match: kEnumerableProperty,\n  has: kEnumerableProperty,\n  open: kEnumerableProperty,\n  delete: kEnumerableProperty,\n  keys: kEnumerableProperty\n})\n\nmodule.exports = {\n  CacheStorage\n}\n"
  },
  {
    "path": "lib/web/cache/util.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { URLSerializer } = require('../fetch/data-url')\nconst { isValidHeaderName } = require('../fetch/util')\n\n/**\n * @see https://url.spec.whatwg.org/#concept-url-equals\n * @param {URL} A\n * @param {URL} B\n * @param {boolean | undefined} excludeFragment\n * @returns {boolean}\n */\nfunction urlEquals (A, B, excludeFragment = false) {\n  const serializedA = URLSerializer(A, excludeFragment)\n\n  const serializedB = URLSerializer(B, excludeFragment)\n\n  return serializedA === serializedB\n}\n\n/**\n * @see https://github.com/chromium/chromium/blob/694d20d134cb553d8d89e5500b9148012b1ba299/content/browser/cache_storage/cache_storage_cache.cc#L260-L262\n * @param {string} header\n */\nfunction getFieldValues (header) {\n  assert(header !== null)\n\n  const values = []\n\n  for (let value of header.split(',')) {\n    value = value.trim()\n\n    if (isValidHeaderName(value)) {\n      values.push(value)\n    }\n  }\n\n  return values\n}\n\nmodule.exports = {\n  urlEquals,\n  getFieldValues\n}\n"
  },
  {
    "path": "lib/web/cookies/constants.js",
    "content": "'use strict'\n\n// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size\nconst maxAttributeValueSize = 1024\n\n// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size\nconst maxNameValuePairSize = 4096\n\nmodule.exports = {\n  maxAttributeValueSize,\n  maxNameValuePairSize\n}\n"
  },
  {
    "path": "lib/web/cookies/index.js",
    "content": "'use strict'\n\nconst { parseSetCookie } = require('./parse')\nconst { stringify } = require('./util')\nconst { webidl } = require('../webidl')\nconst { Headers } = require('../fetch/headers')\n\nconst brandChecks = webidl.brandCheckMultiple([Headers, globalThis.Headers].filter(Boolean))\n\n/**\n * @typedef {Object} Cookie\n * @property {string} name\n * @property {string} value\n * @property {Date|number} [expires]\n * @property {number} [maxAge]\n * @property {string} [domain]\n * @property {string} [path]\n * @property {boolean} [secure]\n * @property {boolean} [httpOnly]\n * @property {'Strict'|'Lax'|'None'} [sameSite]\n * @property {string[]} [unparsed]\n */\n\n/**\n * @param {Headers} headers\n * @returns {Record<string, string>}\n */\nfunction getCookies (headers) {\n  webidl.argumentLengthCheck(arguments, 1, 'getCookies')\n\n  brandChecks(headers)\n\n  const cookie = headers.get('cookie')\n\n  /** @type {Record<string, string>} */\n  const out = {}\n\n  if (!cookie) {\n    return out\n  }\n\n  for (const piece of cookie.split(';')) {\n    const [name, ...value] = piece.split('=')\n\n    out[name.trim()] = value.join('=')\n  }\n\n  return out\n}\n\n/**\n * @param {Headers} headers\n * @param {string} name\n * @param {{ path?: string, domain?: string }|undefined} attributes\n * @returns {void}\n */\nfunction deleteCookie (headers, name, attributes) {\n  brandChecks(headers)\n\n  const prefix = 'deleteCookie'\n  webidl.argumentLengthCheck(arguments, 2, prefix)\n\n  name = webidl.converters.DOMString(name, prefix, 'name')\n  attributes = webidl.converters.DeleteCookieAttributes(attributes)\n\n  // Matches behavior of\n  // https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278\n  setCookie(headers, {\n    name,\n    value: '',\n    expires: new Date(0),\n    ...attributes\n  })\n}\n\n/**\n * @param {Headers} headers\n * @returns {Cookie[]}\n */\nfunction getSetCookies (headers) {\n  webidl.argumentLengthCheck(arguments, 1, 'getSetCookies')\n\n  brandChecks(headers)\n\n  const cookies = headers.getSetCookie()\n\n  if (!cookies) {\n    return []\n  }\n\n  return cookies.map((pair) => parseSetCookie(pair))\n}\n\n/**\n * Parses a cookie string\n * @param {string} cookie\n */\nfunction parseCookie (cookie) {\n  cookie = webidl.converters.DOMString(cookie)\n\n  return parseSetCookie(cookie)\n}\n\n/**\n * @param {Headers} headers\n * @param {Cookie} cookie\n * @returns {void}\n */\nfunction setCookie (headers, cookie) {\n  webidl.argumentLengthCheck(arguments, 2, 'setCookie')\n\n  brandChecks(headers)\n\n  cookie = webidl.converters.Cookie(cookie)\n\n  const str = stringify(cookie)\n\n  if (str) {\n    headers.append('set-cookie', str, true)\n  }\n}\n\nwebidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([\n  {\n    converter: webidl.nullableConverter(webidl.converters.DOMString),\n    key: 'path',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters.DOMString),\n    key: 'domain',\n    defaultValue: () => null\n  }\n])\n\nwebidl.converters.Cookie = webidl.dictionaryConverter([\n  {\n    converter: webidl.converters.DOMString,\n    key: 'name'\n  },\n  {\n    converter: webidl.converters.DOMString,\n    key: 'value'\n  },\n  {\n    converter: webidl.nullableConverter((value) => {\n      if (typeof value === 'number') {\n        return webidl.converters['unsigned long long'](value)\n      }\n\n      return new Date(value)\n    }),\n    key: 'expires',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters['long long']),\n    key: 'maxAge',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters.DOMString),\n    key: 'domain',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters.DOMString),\n    key: 'path',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters.boolean),\n    key: 'secure',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.nullableConverter(webidl.converters.boolean),\n    key: 'httpOnly',\n    defaultValue: () => null\n  },\n  {\n    converter: webidl.converters.USVString,\n    key: 'sameSite',\n    allowedValues: ['Strict', 'Lax', 'None']\n  },\n  {\n    converter: webidl.sequenceConverter(webidl.converters.DOMString),\n    key: 'unparsed',\n    defaultValue: () => []\n  }\n])\n\nmodule.exports = {\n  getCookies,\n  deleteCookie,\n  getSetCookies,\n  setCookie,\n  parseCookie\n}\n"
  },
  {
    "path": "lib/web/cookies/parse.js",
    "content": "'use strict'\n\nconst { collectASequenceOfCodePointsFast } = require('../infra')\nconst { maxNameValuePairSize, maxAttributeValueSize } = require('./constants')\nconst { isCTLExcludingHtab } = require('./util')\nconst assert = require('node:assert')\nconst { unescape: qsUnescape } = require('node:querystring')\n\n/**\n * @description Parses the field-value attributes of a set-cookie header string.\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4\n * @param {string} header\n * @returns {import('./index').Cookie|null} if the header is invalid, null will be returned\n */\nfunction parseSetCookie (header) {\n  // 1. If the set-cookie-string contains a %x00-08 / %x0A-1F / %x7F\n  //    character (CTL characters excluding HTAB): Abort these steps and\n  //    ignore the set-cookie-string entirely.\n  if (isCTLExcludingHtab(header)) {\n    return null\n  }\n\n  let nameValuePair = ''\n  let unparsedAttributes = ''\n  let name = ''\n  let value = ''\n\n  // 2. If the set-cookie-string contains a %x3B (\";\") character:\n  if (header.includes(';')) {\n    // 1. The name-value-pair string consists of the characters up to,\n    //    but not including, the first %x3B (\";\"), and the unparsed-\n    //    attributes consist of the remainder of the set-cookie-string\n    //    (including the %x3B (\";\") in question).\n    const position = { position: 0 }\n\n    nameValuePair = collectASequenceOfCodePointsFast(';', header, position)\n    unparsedAttributes = header.slice(position.position)\n  } else {\n    // Otherwise:\n\n    // 1. The name-value-pair string consists of all the characters\n    //    contained in the set-cookie-string, and the unparsed-\n    //    attributes is the empty string.\n    nameValuePair = header\n  }\n\n  // 3. If the name-value-pair string lacks a %x3D (\"=\") character, then\n  //    the name string is empty, and the value string is the value of\n  //    name-value-pair.\n  if (!nameValuePair.includes('=')) {\n    value = nameValuePair\n  } else {\n    //    Otherwise, the name string consists of the characters up to, but\n    //    not including, the first %x3D (\"=\") character, and the (possibly\n    //    empty) value string consists of the characters after the first\n    //    %x3D (\"=\") character.\n    const position = { position: 0 }\n    name = collectASequenceOfCodePointsFast(\n      '=',\n      nameValuePair,\n      position\n    )\n    value = nameValuePair.slice(position.position + 1)\n  }\n\n  // 4. Remove any leading or trailing WSP characters from the name\n  //    string and the value string.\n  name = name.trim()\n  value = value.trim()\n\n  // 5. If the sum of the lengths of the name string and the value string\n  //    is more than 4096 octets, abort these steps and ignore the set-\n  //    cookie-string entirely.\n  if (name.length + value.length > maxNameValuePairSize) {\n    return null\n  }\n\n  // 6. The cookie-name is the name string, and the cookie-value is the\n  //    value string.\n  // https://datatracker.ietf.org/doc/html/rfc6265\n  // To maximize compatibility with user agents, servers that wish to\n  // store arbitrary data in a cookie-value SHOULD encode that data, for\n  // example, using Base64 [RFC4648].\n  return {\n    name, value: qsUnescape(value), ...parseUnparsedAttributes(unparsedAttributes)\n  }\n}\n\n/**\n * Parses the remaining attributes of a set-cookie header\n * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4\n * @param {string} unparsedAttributes\n * @param {Object.<string, unknown>} [cookieAttributeList={}]\n */\nfunction parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) {\n  // 1. If the unparsed-attributes string is empty, skip the rest of\n  //    these steps.\n  if (unparsedAttributes.length === 0) {\n    return cookieAttributeList\n  }\n\n  // 2. Discard the first character of the unparsed-attributes (which\n  //    will be a %x3B (\";\") character).\n  assert(unparsedAttributes[0] === ';')\n  unparsedAttributes = unparsedAttributes.slice(1)\n\n  let cookieAv = ''\n\n  // 3. If the remaining unparsed-attributes contains a %x3B (\";\")\n  //    character:\n  if (unparsedAttributes.includes(';')) {\n    // 1. Consume the characters of the unparsed-attributes up to, but\n    //    not including, the first %x3B (\";\") character.\n    cookieAv = collectASequenceOfCodePointsFast(\n      ';',\n      unparsedAttributes,\n      { position: 0 }\n    )\n    unparsedAttributes = unparsedAttributes.slice(cookieAv.length)\n  } else {\n    // Otherwise:\n\n    // 1. Consume the remainder of the unparsed-attributes.\n    cookieAv = unparsedAttributes\n    unparsedAttributes = ''\n  }\n\n  // Let the cookie-av string be the characters consumed in this step.\n\n  let attributeName = ''\n  let attributeValue = ''\n\n  // 4. If the cookie-av string contains a %x3D (\"=\") character:\n  if (cookieAv.includes('=')) {\n    // 1. The (possibly empty) attribute-name string consists of the\n    //    characters up to, but not including, the first %x3D (\"=\")\n    //    character, and the (possibly empty) attribute-value string\n    //    consists of the characters after the first %x3D (\"=\")\n    //    character.\n    const position = { position: 0 }\n\n    attributeName = collectASequenceOfCodePointsFast(\n      '=',\n      cookieAv,\n      position\n    )\n    attributeValue = cookieAv.slice(position.position + 1)\n  } else {\n    // Otherwise:\n\n    // 1. The attribute-name string consists of the entire cookie-av\n    //    string, and the attribute-value string is empty.\n    attributeName = cookieAv\n  }\n\n  // 5. Remove any leading or trailing WSP characters from the attribute-\n  //    name string and the attribute-value string.\n  attributeName = attributeName.trim()\n  attributeValue = attributeValue.trim()\n\n  // 6. If the attribute-value is longer than 1024 octets, ignore the\n  //    cookie-av string and return to Step 1 of this algorithm.\n  if (attributeValue.length > maxAttributeValueSize) {\n    return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)\n  }\n\n  // 7. Process the attribute-name and attribute-value according to the\n  //    requirements in the following subsections.  (Notice that\n  //    attributes with unrecognized attribute-names are ignored.)\n  const attributeNameLowercase = attributeName.toLowerCase()\n\n  // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.1\n  // If the attribute-name case-insensitively matches the string\n  // \"Expires\", the user agent MUST process the cookie-av as follows.\n  if (attributeNameLowercase === 'expires') {\n    // 1. Let the expiry-time be the result of parsing the attribute-value\n    //    as cookie-date (see Section 5.1.1).\n    const expiryTime = new Date(attributeValue)\n\n    // 2. If the attribute-value failed to parse as a cookie date, ignore\n    //    the cookie-av.\n\n    cookieAttributeList.expires = expiryTime\n  } else if (attributeNameLowercase === 'max-age') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.2\n    // If the attribute-name case-insensitively matches the string \"Max-\n    // Age\", the user agent MUST process the cookie-av as follows.\n\n    // 1. If the first character of the attribute-value is not a DIGIT or a\n    //    \"-\" character, ignore the cookie-av.\n    const charCode = attributeValue.charCodeAt(0)\n\n    if ((charCode < 48 || charCode > 57) && attributeValue[0] !== '-') {\n      return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)\n    }\n\n    // 2. If the remainder of attribute-value contains a non-DIGIT\n    //    character, ignore the cookie-av.\n    if (!/^\\d+$/.test(attributeValue)) {\n      return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)\n    }\n\n    // 3. Let delta-seconds be the attribute-value converted to an integer.\n    const deltaSeconds = Number(attributeValue)\n\n    // 4. Let cookie-age-limit be the maximum age of the cookie (which\n    //    SHOULD be 400 days or less, see Section 4.1.2.2).\n\n    // 5. Set delta-seconds to the smaller of its present value and cookie-\n    //    age-limit.\n    // deltaSeconds = Math.min(deltaSeconds * 1000, maxExpiresMs)\n\n    // 6. If delta-seconds is less than or equal to zero (0), let expiry-\n    //    time be the earliest representable date and time.  Otherwise, let\n    //    the expiry-time be the current date and time plus delta-seconds\n    //    seconds.\n    // const expiryTime = deltaSeconds <= 0 ? Date.now() : Date.now() + deltaSeconds\n\n    // 7. Append an attribute to the cookie-attribute-list with an\n    //    attribute-name of Max-Age and an attribute-value of expiry-time.\n    cookieAttributeList.maxAge = deltaSeconds\n  } else if (attributeNameLowercase === 'domain') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.3\n    // If the attribute-name case-insensitively matches the string \"Domain\",\n    // the user agent MUST process the cookie-av as follows.\n\n    // 1. Let cookie-domain be the attribute-value.\n    let cookieDomain = attributeValue\n\n    // 2. If cookie-domain starts with %x2E (\".\"), let cookie-domain be\n    //    cookie-domain without its leading %x2E (\".\").\n    if (cookieDomain[0] === '.') {\n      cookieDomain = cookieDomain.slice(1)\n    }\n\n    // 3. Convert the cookie-domain to lower case.\n    cookieDomain = cookieDomain.toLowerCase()\n\n    // 4. Append an attribute to the cookie-attribute-list with an\n    //    attribute-name of Domain and an attribute-value of cookie-domain.\n    cookieAttributeList.domain = cookieDomain\n  } else if (attributeNameLowercase === 'path') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.4\n    // If the attribute-name case-insensitively matches the string \"Path\",\n    // the user agent MUST process the cookie-av as follows.\n\n    // 1. If the attribute-value is empty or if the first character of the\n    //    attribute-value is not %x2F (\"/\"):\n    let cookiePath = ''\n    if (attributeValue.length === 0 || attributeValue[0] !== '/') {\n      // 1. Let cookie-path be the default-path.\n      cookiePath = '/'\n    } else {\n      // Otherwise:\n\n      // 1. Let cookie-path be the attribute-value.\n      cookiePath = attributeValue\n    }\n\n    // 2. Append an attribute to the cookie-attribute-list with an\n    //    attribute-name of Path and an attribute-value of cookie-path.\n    cookieAttributeList.path = cookiePath\n  } else if (attributeNameLowercase === 'secure') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.5\n    // If the attribute-name case-insensitively matches the string \"Secure\",\n    // the user agent MUST append an attribute to the cookie-attribute-list\n    // with an attribute-name of Secure and an empty attribute-value.\n\n    cookieAttributeList.secure = true\n  } else if (attributeNameLowercase === 'httponly') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.6\n    // If the attribute-name case-insensitively matches the string\n    // \"HttpOnly\", the user agent MUST append an attribute to the cookie-\n    // attribute-list with an attribute-name of HttpOnly and an empty\n    // attribute-value.\n\n    cookieAttributeList.httpOnly = true\n  } else if (attributeNameLowercase === 'samesite') {\n    // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.7\n    // If the attribute-name case-insensitively matches the string\n    // \"SameSite\", the user agent MUST process the cookie-av as follows:\n\n    // 1. Let enforcement be \"Default\".\n    let enforcement = 'Default'\n\n    const attributeValueLowercase = attributeValue.toLowerCase()\n    // 2. If cookie-av's attribute-value is a case-insensitive match for\n    //    \"None\", set enforcement to \"None\".\n    if (attributeValueLowercase.includes('none')) {\n      enforcement = 'None'\n    }\n\n    // 3. If cookie-av's attribute-value is a case-insensitive match for\n    //    \"Strict\", set enforcement to \"Strict\".\n    if (attributeValueLowercase.includes('strict')) {\n      enforcement = 'Strict'\n    }\n\n    // 4. If cookie-av's attribute-value is a case-insensitive match for\n    //    \"Lax\", set enforcement to \"Lax\".\n    if (attributeValueLowercase.includes('lax')) {\n      enforcement = 'Lax'\n    }\n\n    // 5. Append an attribute to the cookie-attribute-list with an\n    //    attribute-name of \"SameSite\" and an attribute-value of\n    //    enforcement.\n    cookieAttributeList.sameSite = enforcement\n  } else {\n    cookieAttributeList.unparsed ??= []\n\n    cookieAttributeList.unparsed.push(`${attributeName}=${attributeValue}`)\n  }\n\n  // 8. Return to Step 1 of this algorithm.\n  return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList)\n}\n\nmodule.exports = {\n  parseSetCookie,\n  parseUnparsedAttributes\n}\n"
  },
  {
    "path": "lib/web/cookies/util.js",
    "content": "'use strict'\n\n/**\n * @param {string} value\n * @returns {boolean}\n */\nfunction isCTLExcludingHtab (value) {\n  for (let i = 0; i < value.length; ++i) {\n    const code = value.charCodeAt(i)\n\n    if (\n      (code >= 0x00 && code <= 0x08) ||\n      (code >= 0x0A && code <= 0x1F) ||\n      code === 0x7F\n    ) {\n      return true\n    }\n  }\n  return false\n}\n\n/**\n CHAR           = <any US-ASCII character (octets 0 - 127)>\n token          = 1*<any CHAR except CTLs or separators>\n separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n                | \",\" | \";\" | \":\" | \"\\\" | <\">\n                | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n                | \"{\" | \"}\" | SP | HT\n * @param {string} name\n */\nfunction validateCookieName (name) {\n  for (let i = 0; i < name.length; ++i) {\n    const code = name.charCodeAt(i)\n\n    if (\n      code < 0x21 || // exclude CTLs (0-31), SP and HT\n      code > 0x7E || // exclude non-ascii and DEL\n      code === 0x22 || // \"\n      code === 0x28 || // (\n      code === 0x29 || // )\n      code === 0x3C || // <\n      code === 0x3E || // >\n      code === 0x40 || // @\n      code === 0x2C || // ,\n      code === 0x3B || // ;\n      code === 0x3A || // :\n      code === 0x5C || // \\\n      code === 0x2F || // /\n      code === 0x5B || // [\n      code === 0x5D || // ]\n      code === 0x3F || // ?\n      code === 0x3D || // =\n      code === 0x7B || // {\n      code === 0x7D // }\n    ) {\n      throw new Error('Invalid cookie name')\n    }\n  }\n}\n\n/**\n cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )\n cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E\n                       ; US-ASCII characters excluding CTLs,\n                       ; whitespace DQUOTE, comma, semicolon,\n                       ; and backslash\n * @param {string} value\n */\nfunction validateCookieValue (value) {\n  let len = value.length\n  let i = 0\n\n  // if the value is wrapped in DQUOTE\n  if (value[0] === '\"') {\n    if (len === 1 || value[len - 1] !== '\"') {\n      throw new Error('Invalid cookie value')\n    }\n    --len\n    ++i\n  }\n\n  while (i < len) {\n    const code = value.charCodeAt(i++)\n\n    if (\n      code < 0x21 || // exclude CTLs (0-31)\n      code > 0x7E || // non-ascii and DEL (127)\n      code === 0x22 || // \"\n      code === 0x2C || // ,\n      code === 0x3B || // ;\n      code === 0x5C // \\\n    ) {\n      throw new Error('Invalid cookie value')\n    }\n  }\n}\n\n/**\n * path-value        = <any CHAR except CTLs or \";\">\n * @param {string} path\n */\nfunction validateCookiePath (path) {\n  for (let i = 0; i < path.length; ++i) {\n    const code = path.charCodeAt(i)\n\n    if (\n      code < 0x20 || // exclude CTLs (0-31)\n      code === 0x7F || // DEL\n      code === 0x3B // ;\n    ) {\n      throw new Error('Invalid cookie path')\n    }\n  }\n}\n\n/**\n * I have no idea why these values aren't allowed to be honest,\n * but Deno tests these. - Khafra\n * @param {string} domain\n */\nfunction validateCookieDomain (domain) {\n  if (\n    domain.startsWith('-') ||\n    domain.endsWith('.') ||\n    domain.endsWith('-')\n  ) {\n    throw new Error('Invalid cookie domain')\n  }\n}\n\nconst IMFDays = [\n  'Sun', 'Mon', 'Tue', 'Wed',\n  'Thu', 'Fri', 'Sat'\n]\n\nconst IMFMonths = [\n  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'\n]\n\nconst IMFPaddedNumbers = Array(61).fill(0).map((_, i) => i.toString().padStart(2, '0'))\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc7231#section-7.1.1.1\n * @param {number|Date} date\n  IMF-fixdate  = day-name \",\" SP date1 SP time-of-day SP GMT\n  ; fixed length/zone/capitalization subset of the format\n  ; see Section 3.3 of [RFC5322]\n\n  day-name     = %x4D.6F.6E ; \"Mon\", case-sensitive\n              / %x54.75.65 ; \"Tue\", case-sensitive\n              / %x57.65.64 ; \"Wed\", case-sensitive\n              / %x54.68.75 ; \"Thu\", case-sensitive\n              / %x46.72.69 ; \"Fri\", case-sensitive\n              / %x53.61.74 ; \"Sat\", case-sensitive\n              / %x53.75.6E ; \"Sun\", case-sensitive\n  date1        = day SP month SP year\n                  ; e.g., 02 Jun 1982\n\n  day          = 2DIGIT\n  month        = %x4A.61.6E ; \"Jan\", case-sensitive\n              / %x46.65.62 ; \"Feb\", case-sensitive\n              / %x4D.61.72 ; \"Mar\", case-sensitive\n              / %x41.70.72 ; \"Apr\", case-sensitive\n              / %x4D.61.79 ; \"May\", case-sensitive\n              / %x4A.75.6E ; \"Jun\", case-sensitive\n              / %x4A.75.6C ; \"Jul\", case-sensitive\n              / %x41.75.67 ; \"Aug\", case-sensitive\n              / %x53.65.70 ; \"Sep\", case-sensitive\n              / %x4F.63.74 ; \"Oct\", case-sensitive\n              / %x4E.6F.76 ; \"Nov\", case-sensitive\n              / %x44.65.63 ; \"Dec\", case-sensitive\n  year         = 4DIGIT\n\n  GMT          = %x47.4D.54 ; \"GMT\", case-sensitive\n\n  time-of-day  = hour \":\" minute \":\" second\n              ; 00:00:00 - 23:59:60 (leap second)\n\n  hour         = 2DIGIT\n  minute       = 2DIGIT\n  second       = 2DIGIT\n */\nfunction toIMFDate (date) {\n  if (typeof date === 'number') {\n    date = new Date(date)\n  }\n\n  return `${IMFDays[date.getUTCDay()]}, ${IMFPaddedNumbers[date.getUTCDate()]} ${IMFMonths[date.getUTCMonth()]} ${date.getUTCFullYear()} ${IMFPaddedNumbers[date.getUTCHours()]}:${IMFPaddedNumbers[date.getUTCMinutes()]}:${IMFPaddedNumbers[date.getUTCSeconds()]} GMT`\n}\n\n/**\n max-age-av        = \"Max-Age=\" non-zero-digit *DIGIT\n                       ; In practice, both expires-av and max-age-av\n                       ; are limited to dates representable by the\n                       ; user agent.\n * @param {number} maxAge\n */\nfunction validateCookieMaxAge (maxAge) {\n  if (maxAge < 0) {\n    throw new Error('Invalid cookie max-age')\n  }\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1\n * @param {import('./index').Cookie} cookie\n */\nfunction stringify (cookie) {\n  if (cookie.name.length === 0) {\n    return null\n  }\n\n  validateCookieName(cookie.name)\n  validateCookieValue(cookie.value)\n\n  const out = [`${cookie.name}=${cookie.value}`]\n\n  // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1\n  // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.2\n  if (cookie.name.startsWith('__Secure-')) {\n    cookie.secure = true\n  }\n\n  if (cookie.name.startsWith('__Host-')) {\n    cookie.secure = true\n    cookie.domain = null\n    cookie.path = '/'\n  }\n\n  if (cookie.secure) {\n    out.push('Secure')\n  }\n\n  if (cookie.httpOnly) {\n    out.push('HttpOnly')\n  }\n\n  if (typeof cookie.maxAge === 'number') {\n    validateCookieMaxAge(cookie.maxAge)\n    out.push(`Max-Age=${cookie.maxAge}`)\n  }\n\n  if (cookie.domain) {\n    validateCookieDomain(cookie.domain)\n    out.push(`Domain=${cookie.domain}`)\n  }\n\n  if (cookie.path) {\n    validateCookiePath(cookie.path)\n    out.push(`Path=${cookie.path}`)\n  }\n\n  if (cookie.expires && cookie.expires.toString() !== 'Invalid Date') {\n    out.push(`Expires=${toIMFDate(cookie.expires)}`)\n  }\n\n  if (cookie.sameSite) {\n    out.push(`SameSite=${cookie.sameSite}`)\n  }\n\n  for (const part of cookie.unparsed) {\n    if (!part.includes('=')) {\n      throw new Error('Invalid unparsed')\n    }\n\n    const [key, ...value] = part.split('=')\n\n    out.push(`${key.trim()}=${value.join('=')}`)\n  }\n\n  return out.join('; ')\n}\n\nmodule.exports = {\n  isCTLExcludingHtab,\n  validateCookieName,\n  validateCookiePath,\n  validateCookieValue,\n  toIMFDate,\n  stringify\n}\n"
  },
  {
    "path": "lib/web/eventsource/eventsource-stream.js",
    "content": "'use strict'\nconst { Transform } = require('node:stream')\nconst { isASCIINumber, isValidLastEventId } = require('./util')\n\n/**\n * @type {number[]} BOM\n */\nconst BOM = [0xEF, 0xBB, 0xBF]\n/**\n * @type {10} LF\n */\nconst LF = 0x0A\n/**\n * @type {13} CR\n */\nconst CR = 0x0D\n/**\n * @type {58} COLON\n */\nconst COLON = 0x3A\n/**\n * @type {32} SPACE\n */\nconst SPACE = 0x20\n\n/**\n * @typedef {object} EventSourceStreamEvent\n * @type {object}\n * @property {string} [event] The event type.\n * @property {string} [data] The data of the message.\n * @property {string} [id] A unique ID for the event.\n * @property {string} [retry] The reconnection time, in milliseconds.\n */\n\n/**\n * @typedef eventSourceSettings\n * @type {object}\n * @property {string} [lastEventId] The last event ID received from the server.\n * @property {string} [origin] The origin of the event source.\n * @property {number} [reconnectionTime] The reconnection time, in milliseconds.\n */\n\nclass EventSourceStream extends Transform {\n  /**\n   * @type {eventSourceSettings}\n   */\n  state\n\n  /**\n   * Leading byte-order-mark check.\n   * @type {boolean}\n   */\n  checkBOM = true\n\n  /**\n   * @type {boolean}\n   */\n  crlfCheck = false\n\n  /**\n   * @type {boolean}\n   */\n  eventEndCheck = false\n\n  /**\n   * @type {Buffer|null}\n   */\n  buffer = null\n\n  pos = 0\n\n  event = {\n    data: undefined,\n    event: undefined,\n    id: undefined,\n    retry: undefined\n  }\n\n  /**\n   * @param {object} options\n   * @param {boolean} [options.readableObjectMode]\n   * @param {eventSourceSettings} [options.eventSourceSettings]\n   * @param {(chunk: any, encoding?: BufferEncoding | undefined) => boolean} [options.push]\n   */\n  constructor (options = {}) {\n    // Enable object mode as EventSourceStream emits objects of shape\n    // EventSourceStreamEvent\n    options.readableObjectMode = true\n\n    super(options)\n\n    this.state = options.eventSourceSettings || {}\n    if (options.push) {\n      this.push = options.push\n    }\n  }\n\n  /**\n   * @param {Buffer} chunk\n   * @param {string} _encoding\n   * @param {Function} callback\n   * @returns {void}\n   */\n  _transform (chunk, _encoding, callback) {\n    if (chunk.length === 0) {\n      callback()\n      return\n    }\n\n    // Cache the chunk in the buffer, as the data might not be complete while\n    // processing it\n    // TODO: Investigate if there is a more performant way to handle\n    // incoming chunks\n    // see: https://github.com/nodejs/undici/issues/2630\n    if (this.buffer) {\n      this.buffer = Buffer.concat([this.buffer, chunk])\n    } else {\n      this.buffer = chunk\n    }\n\n    // Strip leading byte-order-mark if we opened the stream and started\n    // the processing of the incoming data\n    if (this.checkBOM) {\n      switch (this.buffer.length) {\n        case 1:\n          // Check if the first byte is the same as the first byte of the BOM\n          if (this.buffer[0] === BOM[0]) {\n            // If it is, we need to wait for more data\n            callback()\n            return\n          }\n          // Set the checkBOM flag to false as we don't need to check for the\n          // BOM anymore\n          this.checkBOM = false\n\n          // The buffer only contains one byte so we need to wait for more data\n          callback()\n          return\n        case 2:\n          // Check if the first two bytes are the same as the first two bytes\n          // of the BOM\n          if (\n            this.buffer[0] === BOM[0] &&\n            this.buffer[1] === BOM[1]\n          ) {\n            // If it is, we need to wait for more data, because the third byte\n            // is needed to determine if it is the BOM or not\n            callback()\n            return\n          }\n\n          // Set the checkBOM flag to false as we don't need to check for the\n          // BOM anymore\n          this.checkBOM = false\n          break\n        case 3:\n          // Check if the first three bytes are the same as the first three\n          // bytes of the BOM\n          if (\n            this.buffer[0] === BOM[0] &&\n            this.buffer[1] === BOM[1] &&\n            this.buffer[2] === BOM[2]\n          ) {\n            // If it is, we can drop the buffered data, as it is only the BOM\n            this.buffer = Buffer.alloc(0)\n            // Set the checkBOM flag to false as we don't need to check for the\n            // BOM anymore\n            this.checkBOM = false\n\n            // Await more data\n            callback()\n            return\n          }\n          // If it is not the BOM, we can start processing the data\n          this.checkBOM = false\n          break\n        default:\n          // The buffer is longer than 3 bytes, so we can drop the BOM if it is\n          // present\n          if (\n            this.buffer[0] === BOM[0] &&\n            this.buffer[1] === BOM[1] &&\n            this.buffer[2] === BOM[2]\n          ) {\n            // Remove the BOM from the buffer\n            this.buffer = this.buffer.subarray(3)\n          }\n\n          // Set the checkBOM flag to false as we don't need to check for the\n          this.checkBOM = false\n          break\n      }\n    }\n\n    while (this.pos < this.buffer.length) {\n      // If the previous line ended with an end-of-line, we need to check\n      // if the next character is also an end-of-line.\n      if (this.eventEndCheck) {\n        // If the the current character is an end-of-line, then the event\n        // is finished and we can process it\n\n        // If the previous line ended with a carriage return, we need to\n        // check if the current character is a line feed and remove it\n        // from the buffer.\n        if (this.crlfCheck) {\n          // If the current character is a line feed, we can remove it\n          // from the buffer and reset the crlfCheck flag\n          if (this.buffer[this.pos] === LF) {\n            this.buffer = this.buffer.subarray(this.pos + 1)\n            this.pos = 0\n            this.crlfCheck = false\n\n            // It is possible that the line feed is not the end of the\n            // event. We need to check if the next character is an\n            // end-of-line character to determine if the event is\n            // finished. We simply continue the loop to check the next\n            // character.\n\n            // As we removed the line feed from the buffer and set the\n            // crlfCheck flag to false, we basically don't make any\n            // distinction between a line feed and a carriage return.\n            continue\n          }\n          this.crlfCheck = false\n        }\n\n        if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {\n          // If the current character is a carriage return, we need to\n          // set the crlfCheck flag to true, as we need to check if the\n          // next character is a line feed so we can remove it from the\n          // buffer\n          if (this.buffer[this.pos] === CR) {\n            this.crlfCheck = true\n          }\n\n          this.buffer = this.buffer.subarray(this.pos + 1)\n          this.pos = 0\n          if (\n            this.event.data !== undefined || this.event.event || this.event.id !== undefined || this.event.retry) {\n            this.processEvent(this.event)\n          }\n          this.clearEvent()\n          continue\n        }\n        // If the current character is not an end-of-line, then the event\n        // is not finished and we have to reset the eventEndCheck flag\n        this.eventEndCheck = false\n        continue\n      }\n\n      // If the current character is an end-of-line, we can process the\n      // line\n      if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {\n        // If the current character is a carriage return, we need to\n        // set the crlfCheck flag to true, as we need to check if the\n        // next character is a line feed\n        if (this.buffer[this.pos] === CR) {\n          this.crlfCheck = true\n        }\n\n        // In any case, we can process the line as we reached an\n        // end-of-line character\n        this.parseLine(this.buffer.subarray(0, this.pos), this.event)\n\n        // Remove the processed line from the buffer\n        this.buffer = this.buffer.subarray(this.pos + 1)\n        // Reset the position as we removed the processed line from the buffer\n        this.pos = 0\n        // A line was processed and this could be the end of the event. We need\n        // to check if the next line is empty to determine if the event is\n        // finished.\n        this.eventEndCheck = true\n        continue\n      }\n\n      this.pos++\n    }\n\n    callback()\n  }\n\n  /**\n   * @param {Buffer} line\n   * @param {EventSourceStreamEvent} event\n   */\n  parseLine (line, event) {\n    // If the line is empty (a blank line)\n    // Dispatch the event, as defined below.\n    // This will be handled in the _transform method\n    if (line.length === 0) {\n      return\n    }\n\n    // If the line starts with a U+003A COLON character (:)\n    // Ignore the line.\n    const colonPosition = line.indexOf(COLON)\n    if (colonPosition === 0) {\n      return\n    }\n\n    let field = ''\n    let value = ''\n\n    // If the line contains a U+003A COLON character (:)\n    if (colonPosition !== -1) {\n      // Collect the characters on the line before the first U+003A COLON\n      // character (:), and let field be that string.\n      // TODO: Investigate if there is a more performant way to extract the\n      // field\n      // see: https://github.com/nodejs/undici/issues/2630\n      field = line.subarray(0, colonPosition).toString('utf8')\n\n      // Collect the characters on the line after the first U+003A COLON\n      // character (:), and let value be that string.\n      // If value starts with a U+0020 SPACE character, remove it from value.\n      let valueStart = colonPosition + 1\n      if (line[valueStart] === SPACE) {\n        ++valueStart\n      }\n      // TODO: Investigate if there is a more performant way to extract the\n      // value\n      // see: https://github.com/nodejs/undici/issues/2630\n      value = line.subarray(valueStart).toString('utf8')\n\n      // Otherwise, the string is not empty but does not contain a U+003A COLON\n      // character (:)\n    } else {\n      // Process the field using the steps described below, using the whole\n      // line as the field name, and the empty string as the field value.\n      field = line.toString('utf8')\n      value = ''\n    }\n\n    // Modify the event with the field name and value. The value is also\n    // decoded as UTF-8\n    switch (field) {\n      case 'data':\n        if (event[field] === undefined) {\n          event[field] = value\n        } else {\n          event[field] += `\\n${value}`\n        }\n        break\n      case 'retry':\n        if (isASCIINumber(value)) {\n          event[field] = value\n        }\n        break\n      case 'id':\n        if (isValidLastEventId(value)) {\n          event[field] = value\n        }\n        break\n      case 'event':\n        if (value.length > 0) {\n          event[field] = value\n        }\n        break\n    }\n  }\n\n  /**\n   * @param {EventSourceStreamEvent} event\n   */\n  processEvent (event) {\n    if (event.retry && isASCIINumber(event.retry)) {\n      this.state.reconnectionTime = parseInt(event.retry, 10)\n    }\n\n    if (event.id !== undefined && isValidLastEventId(event.id)) {\n      this.state.lastEventId = event.id\n    }\n\n    // only dispatch event, when data is provided\n    if (event.data !== undefined) {\n      this.push({\n        type: event.event || 'message',\n        options: {\n          data: event.data,\n          lastEventId: this.state.lastEventId,\n          origin: this.state.origin\n        }\n      })\n    }\n  }\n\n  clearEvent () {\n    this.event = {\n      data: undefined,\n      event: undefined,\n      id: undefined,\n      retry: undefined\n    }\n  }\n}\n\nmodule.exports = {\n  EventSourceStream\n}\n"
  },
  {
    "path": "lib/web/eventsource/eventsource.js",
    "content": "'use strict'\n\nconst { pipeline } = require('node:stream')\nconst { fetching } = require('../fetch')\nconst { makeRequest } = require('../fetch/request')\nconst { webidl } = require('../webidl')\nconst { EventSourceStream } = require('./eventsource-stream')\nconst { parseMIMEType } = require('../fetch/data-url')\nconst { createFastMessageEvent } = require('../websocket/events')\nconst { isNetworkError } = require('../fetch/response')\nconst { kEnumerableProperty } = require('../../core/util')\nconst { environmentSettingsObject } = require('../fetch/util')\n\nlet experimentalWarned = false\n\n/**\n * A reconnection time, in milliseconds. This must initially be an implementation-defined value,\n * probably in the region of a few seconds.\n *\n * In Comparison:\n * - Chrome uses 3000ms.\n * - Deno uses 5000ms.\n *\n * @type {3000}\n */\nconst defaultReconnectionTime = 3000\n\n/**\n * The readyState attribute represents the state of the connection.\n * @typedef ReadyState\n * @type {0|1|2}\n * @readonly\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#dom-eventsource-readystate-dev\n */\n\n/**\n * The connection has not yet been established, or it was closed and the user\n * agent is reconnecting.\n * @type {0}\n */\nconst CONNECTING = 0\n\n/**\n * The user agent has an open connection and is dispatching events as it\n * receives them.\n * @type {1}\n */\nconst OPEN = 1\n\n/**\n * The connection is not open, and the user agent is not trying to reconnect.\n * @type {2}\n */\nconst CLOSED = 2\n\n/**\n * Requests for the element will have their mode set to \"cors\" and their credentials mode set to \"same-origin\".\n * @type {'anonymous'}\n */\nconst ANONYMOUS = 'anonymous'\n\n/**\n * Requests for the element will have their mode set to \"cors\" and their credentials mode set to \"include\".\n * @type {'use-credentials'}\n */\nconst USE_CREDENTIALS = 'use-credentials'\n\n/**\n * The EventSource interface is used to receive server-sent events. It\n * connects to a server over HTTP and receives events in text/event-stream\n * format without closing the connection.\n * @extends {EventTarget}\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events\n * @api public\n */\nclass EventSource extends EventTarget {\n  #events = {\n    open: null,\n    error: null,\n    message: null\n  }\n\n  #url\n  #withCredentials = false\n\n  /**\n   * @type {ReadyState}\n   */\n  #readyState = CONNECTING\n\n  #request = null\n  #controller = null\n\n  #dispatcher\n\n  /**\n   * @type {import('./eventsource-stream').eventSourceSettings}\n   */\n  #state\n\n  /**\n   * Creates a new EventSource object.\n   * @param {string} url\n   * @param {EventSourceInit} [eventSourceInitDict={}]\n   * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#the-eventsource-interface\n   */\n  constructor (url, eventSourceInitDict = {}) {\n    // 1. Let ev be a new EventSource object.\n    super()\n\n    webidl.util.markAsUncloneable(this)\n\n    const prefix = 'EventSource constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    if (!experimentalWarned) {\n      experimentalWarned = true\n      process.emitWarning('EventSource is experimental, expect them to change at any time.', {\n        code: 'UNDICI-ES'\n      })\n    }\n\n    url = webidl.converters.USVString(url)\n    eventSourceInitDict = webidl.converters.EventSourceInitDict(eventSourceInitDict, prefix, 'eventSourceInitDict')\n\n    this.#dispatcher = eventSourceInitDict.node.dispatcher || eventSourceInitDict.dispatcher\n    this.#state = {\n      lastEventId: '',\n      reconnectionTime: eventSourceInitDict.node.reconnectionTime\n    }\n\n    // 2. Let settings be ev's relevant settings object.\n    // https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object\n    const settings = environmentSettingsObject\n\n    let urlRecord\n\n    try {\n      // 3. Let urlRecord be the result of encoding-parsing a URL given url, relative to settings.\n      urlRecord = new URL(url, settings.settingsObject.baseUrl)\n      this.#state.origin = urlRecord.origin\n    } catch (e) {\n      // 4. If urlRecord is failure, then throw a \"SyntaxError\" DOMException.\n      throw new DOMException(e, 'SyntaxError')\n    }\n\n    // 5. Set ev's url to urlRecord.\n    this.#url = urlRecord.href\n\n    // 6. Let corsAttributeState be Anonymous.\n    let corsAttributeState = ANONYMOUS\n\n    // 7. If the value of eventSourceInitDict's withCredentials member is true,\n    // then set corsAttributeState to Use Credentials and set ev's\n    // withCredentials attribute to true.\n    if (eventSourceInitDict.withCredentials === true) {\n      corsAttributeState = USE_CREDENTIALS\n      this.#withCredentials = true\n    }\n\n    // 8. Let request be the result of creating a potential-CORS request given\n    // urlRecord, the empty string, and corsAttributeState.\n    const initRequest = {\n      redirect: 'follow',\n      keepalive: true,\n      // @see https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes\n      mode: 'cors',\n      credentials: corsAttributeState === 'anonymous'\n        ? 'same-origin'\n        : 'omit',\n      referrer: 'no-referrer'\n    }\n\n    // 9. Set request's client to settings.\n    initRequest.client = environmentSettingsObject.settingsObject\n\n    // 10. User agents may set (`Accept`, `text/event-stream`) in request's header list.\n    initRequest.headersList = [['accept', { name: 'accept', value: 'text/event-stream' }]]\n\n    // 11. Set request's cache mode to \"no-store\".\n    initRequest.cache = 'no-store'\n\n    // 12. Set request's initiator type to \"other\".\n    initRequest.initiator = 'other'\n\n    initRequest.urlList = [new URL(this.#url)]\n\n    // 13. Set ev's request to request.\n    this.#request = makeRequest(initRequest)\n\n    this.#connect()\n  }\n\n  /**\n   * Returns the state of this EventSource object's connection. It can have the\n   * values described below.\n   * @returns {ReadyState}\n   * @readonly\n   */\n  get readyState () {\n    return this.#readyState\n  }\n\n  /**\n   * Returns the URL providing the event stream.\n   * @readonly\n   * @returns {string}\n   */\n  get url () {\n    return this.#url\n  }\n\n  /**\n   * Returns a boolean indicating whether the EventSource object was\n   * instantiated with CORS credentials set (true), or not (false, the default).\n   */\n  get withCredentials () {\n    return this.#withCredentials\n  }\n\n  #connect () {\n    if (this.#readyState === CLOSED) return\n\n    this.#readyState = CONNECTING\n\n    const fetchParams = {\n      request: this.#request,\n      dispatcher: this.#dispatcher\n    }\n\n    // 14. Let processEventSourceEndOfBody given response res be the following step: if res is not a network error, then reestablish the connection.\n    const processEventSourceEndOfBody = (response) => {\n      if (!isNetworkError(response)) {\n        return this.#reconnect()\n      }\n    }\n\n    // 15. Fetch request, with processResponseEndOfBody set to processEventSourceEndOfBody...\n    fetchParams.processResponseEndOfBody = processEventSourceEndOfBody\n\n    // and processResponse set to the following steps given response res:\n    fetchParams.processResponse = (response) => {\n      // 1. If res is an aborted network error, then fail the connection.\n\n      if (isNetworkError(response)) {\n        // 1. When a user agent is to fail the connection, the user agent\n        // must queue a task which, if the readyState attribute is set to a\n        // value other than CLOSED, sets the readyState attribute to CLOSED\n        // and fires an event named error at the EventSource object. Once the\n        // user agent has failed the connection, it does not attempt to\n        // reconnect.\n        if (response.aborted) {\n          this.close()\n          this.dispatchEvent(new Event('error'))\n          return\n          // 2. Otherwise, if res is a network error, then reestablish the\n          // connection, unless the user agent knows that to be futile, in\n          // which case the user agent may fail the connection.\n        } else {\n          this.#reconnect()\n          return\n        }\n      }\n\n      // 3. Otherwise, if res's status is not 200, or if res's `Content-Type`\n      // is not `text/event-stream`, then fail the connection.\n      const contentType = response.headersList.get('content-type', true)\n      const mimeType = contentType !== null ? parseMIMEType(contentType) : 'failure'\n      const contentTypeValid = mimeType !== 'failure' && mimeType.essence === 'text/event-stream'\n      if (\n        response.status !== 200 ||\n        contentTypeValid === false\n      ) {\n        this.close()\n        this.dispatchEvent(new Event('error'))\n        return\n      }\n\n      // 4. Otherwise, announce the connection and interpret res's body\n      // line by line.\n\n      // When a user agent is to announce the connection, the user agent\n      // must queue a task which, if the readyState attribute is set to a\n      // value other than CLOSED, sets the readyState attribute to OPEN\n      // and fires an event named open at the EventSource object.\n      // @see https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model\n      this.#readyState = OPEN\n      this.dispatchEvent(new Event('open'))\n\n      // If redirected to a different origin, set the origin to the new origin.\n      this.#state.origin = response.urlList[response.urlList.length - 1].origin\n\n      const eventSourceStream = new EventSourceStream({\n        eventSourceSettings: this.#state,\n        push: (event) => {\n          this.dispatchEvent(createFastMessageEvent(\n            event.type,\n            event.options\n          ))\n        }\n      })\n\n      pipeline(response.body.stream,\n        eventSourceStream,\n        (error) => {\n          if (\n            error?.aborted === false\n          ) {\n            this.close()\n            this.dispatchEvent(new Event('error'))\n          }\n        })\n    }\n\n    this.#controller = fetching(fetchParams)\n  }\n\n  /**\n   * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model\n   * @returns {void}\n   */\n  #reconnect () {\n    // When a user agent is to reestablish the connection, the user agent must\n    // run the following steps. These steps are run in parallel, not as part of\n    // a task. (The tasks that it queues, of course, are run like normal tasks\n    // and not themselves in parallel.)\n\n    // 1. Queue a task to run the following steps:\n\n    //   1. If the readyState attribute is set to CLOSED, abort the task.\n    if (this.#readyState === CLOSED) return\n\n    //   2. Set the readyState attribute to CONNECTING.\n    this.#readyState = CONNECTING\n\n    //   3. Fire an event named error at the EventSource object.\n    this.dispatchEvent(new Event('error'))\n\n    // 2. Wait a delay equal to the reconnection time of the event source.\n    setTimeout(() => {\n      // 5. Queue a task to run the following steps:\n\n      //   1. If the EventSource object's readyState attribute is not set to\n      //      CONNECTING, then return.\n      if (this.#readyState !== CONNECTING) return\n\n      //   2. Let request be the EventSource object's request.\n      //   3. If the EventSource object's last event ID string is not the empty\n      //      string, then:\n      //      1. Let lastEventIDValue be the EventSource object's last event ID\n      //         string, encoded as UTF-8.\n      //      2. Set (`Last-Event-ID`, lastEventIDValue) in request's header\n      //         list.\n      if (this.#state.lastEventId.length) {\n        this.#request.headersList.set('last-event-id', this.#state.lastEventId, true)\n      }\n\n      //   4. Fetch request and process the response obtained in this fashion, if any, as described earlier in this section.\n      this.#connect()\n    }, this.#state.reconnectionTime)?.unref()\n  }\n\n  /**\n   * Closes the connection, if any, and sets the readyState attribute to\n   * CLOSED.\n   */\n  close () {\n    webidl.brandCheck(this, EventSource)\n\n    if (this.#readyState === CLOSED) return\n    this.#readyState = CLOSED\n    this.#controller.abort()\n    this.#request = null\n  }\n\n  get onopen () {\n    return this.#events.open\n  }\n\n  set onopen (fn) {\n    if (this.#events.open) {\n      this.removeEventListener('open', this.#events.open)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('open', listener)\n      this.#events.open = fn\n    } else {\n      this.#events.open = null\n    }\n  }\n\n  get onmessage () {\n    return this.#events.message\n  }\n\n  set onmessage (fn) {\n    if (this.#events.message) {\n      this.removeEventListener('message', this.#events.message)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('message', listener)\n      this.#events.message = fn\n    } else {\n      this.#events.message = null\n    }\n  }\n\n  get onerror () {\n    return this.#events.error\n  }\n\n  set onerror (fn) {\n    if (this.#events.error) {\n      this.removeEventListener('error', this.#events.error)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('error', listener)\n      this.#events.error = fn\n    } else {\n      this.#events.error = null\n    }\n  }\n}\n\nconst constantsPropertyDescriptors = {\n  CONNECTING: {\n    __proto__: null,\n    configurable: false,\n    enumerable: true,\n    value: CONNECTING,\n    writable: false\n  },\n  OPEN: {\n    __proto__: null,\n    configurable: false,\n    enumerable: true,\n    value: OPEN,\n    writable: false\n  },\n  CLOSED: {\n    __proto__: null,\n    configurable: false,\n    enumerable: true,\n    value: CLOSED,\n    writable: false\n  }\n}\n\nObject.defineProperties(EventSource, constantsPropertyDescriptors)\nObject.defineProperties(EventSource.prototype, constantsPropertyDescriptors)\n\nObject.defineProperties(EventSource.prototype, {\n  close: kEnumerableProperty,\n  onerror: kEnumerableProperty,\n  onmessage: kEnumerableProperty,\n  onopen: kEnumerableProperty,\n  readyState: kEnumerableProperty,\n  url: kEnumerableProperty,\n  withCredentials: kEnumerableProperty\n})\n\nwebidl.converters.EventSourceInitDict = webidl.dictionaryConverter([\n  {\n    key: 'withCredentials',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'dispatcher', // undici only\n    converter: webidl.converters.any\n  },\n  {\n    key: 'node', // undici only\n    converter: webidl.dictionaryConverter([\n      {\n        key: 'reconnectionTime',\n        converter: webidl.converters['unsigned long'],\n        defaultValue: () => defaultReconnectionTime\n      },\n      {\n        key: 'dispatcher',\n        converter: webidl.converters.any\n      }\n    ]),\n    defaultValue: () => ({})\n  }\n])\n\nmodule.exports = {\n  EventSource,\n  defaultReconnectionTime\n}\n"
  },
  {
    "path": "lib/web/eventsource/util.js",
    "content": "'use strict'\n\n/**\n * Checks if the given value is a valid LastEventId.\n * @param {string} value\n * @returns {boolean}\n */\nfunction isValidLastEventId (value) {\n  // LastEventId should not contain U+0000 NULL\n  return value.indexOf('\\u0000') === -1\n}\n\n/**\n * Checks if the given value is a base 10 digit.\n * @param {string} value\n * @returns {boolean}\n */\nfunction isASCIINumber (value) {\n  if (value.length === 0) return false\n  for (let i = 0; i < value.length; i++) {\n    if (value.charCodeAt(i) < 0x30 || value.charCodeAt(i) > 0x39) return false\n  }\n  return true\n}\n\nmodule.exports = {\n  isValidLastEventId,\n  isASCIINumber\n}\n"
  },
  {
    "path": "lib/web/fetch/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Ethan Arrowood\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/web/fetch/body.js",
    "content": "'use strict'\n\nconst util = require('../../core/util')\nconst {\n  ReadableStreamFrom,\n  readableStreamClose,\n  fullyReadBody,\n  extractMimeType\n} = require('./util')\nconst { FormData, setFormDataState } = require('./formdata')\nconst { webidl } = require('../webidl')\nconst assert = require('node:assert')\nconst { isErrored, isDisturbed } = require('node:stream')\nconst { isUint8Array } = require('node:util/types')\nconst { serializeAMimeType } = require('./data-url')\nconst { multipartFormDataParser } = require('./formdata-parser')\nconst { createDeferredPromise } = require('../../util/promise')\nconst { parseJSONFromBytes } = require('../infra')\nconst { utf8DecodeBytes } = require('../../encoding')\nconst { runtimeFeatures } = require('../../util/runtime-features.js')\n\nconst random = runtimeFeatures.has('crypto')\n  ? require('node:crypto').randomInt\n  : (max) => Math.floor(Math.random() * max)\n\nconst textEncoder = new TextEncoder()\nfunction noop () {}\n\nconst streamRegistry = new FinalizationRegistry((weakRef) => {\n  const stream = weakRef.deref()\n  if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {\n    stream.cancel('Response object has been garbage collected').catch(noop)\n  }\n})\n\n/**\n * Extract a body with type from a byte sequence or BodyInit object\n *\n * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from\n * @param {boolean} [keepalive=false] - If true, indicates that the body\n * @returns {[{stream: ReadableStream, source: any, length: number | null}, string | null]} - Returns a tuple containing the body and its type\n *\n * @see https://fetch.spec.whatwg.org/#concept-bodyinit-extract\n */\nfunction extractBody (object, keepalive = false) {\n  // 1. Let stream be null.\n  let stream = null\n  let controller = null\n\n  // 2. If object is a ReadableStream object, then set stream to object.\n  if (webidl.is.ReadableStream(object)) {\n    stream = object\n  } else if (webidl.is.Blob(object)) {\n    // 3. Otherwise, if object is a Blob object, set stream to the\n    //    result of running object’s get stream.\n    stream = object.stream()\n  } else {\n    // 4. Otherwise, set stream to a new ReadableStream object, and set\n    //    up stream with byte reading support.\n    stream = new ReadableStream({\n      pull () {},\n      start (c) {\n        controller = c\n      },\n      cancel () {},\n      type: 'bytes'\n    })\n  }\n\n  // 5. Assert: stream is a ReadableStream object.\n  assert(webidl.is.ReadableStream(stream))\n\n  // 6. Let action be null.\n  let action = null\n\n  // 7. Let source be null.\n  let source = null\n\n  // 8. Let length be null.\n  let length = null\n\n  // 9. Let type be null.\n  let type = null\n\n  // 10. Switch on object:\n  if (typeof object === 'string') {\n    // Set source to the UTF-8 encoding of object.\n    // Note: setting source to a Uint8Array here breaks some mocking assumptions.\n    source = object\n\n    // Set type to `text/plain;charset=UTF-8`.\n    type = 'text/plain;charset=UTF-8'\n  } else if (webidl.is.URLSearchParams(object)) {\n    // URLSearchParams\n\n    // spec says to run application/x-www-form-urlencoded on body.list\n    // this is implemented in Node.js as apart of an URLSearchParams instance toString method\n    // See: https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L490\n    // and https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L1100\n\n    // Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list.\n    source = object.toString()\n\n    // Set type to `application/x-www-form-urlencoded;charset=UTF-8`.\n    type = 'application/x-www-form-urlencoded;charset=UTF-8'\n  } else if (webidl.is.BufferSource(object)) {\n    // Set source to a copy of the bytes held by object.\n    source = webidl.util.getCopyOfBytesHeldByBufferSource(object)\n  } else if (webidl.is.FormData(object)) {\n    const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, '0')}`\n    const prefix = `--${boundary}\\r\\nContent-Disposition: form-data`\n\n    /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */\n    const formdataEscape = (str) =>\n      str.replace(/\\n/g, '%0A').replace(/\\r/g, '%0D').replace(/\"/g, '%22')\n    const normalizeLinefeeds = (value) => value.replace(/\\r?\\n|\\r/g, '\\r\\n')\n\n    // Set action to this step: run the multipart/form-data\n    // encoding algorithm, with object’s entry list and UTF-8.\n    // - This ensures that the body is immutable and can't be changed afterwords\n    // - That the content-length is calculated in advance.\n    // - And that all parts are pre-encoded and ready to be sent.\n\n    const blobParts = []\n    const rn = new Uint8Array([13, 10]) // '\\r\\n'\n    length = 0\n    let hasUnknownSizeValue = false\n\n    for (const [name, value] of object) {\n      if (typeof value === 'string') {\n        const chunk = textEncoder.encode(prefix +\n          `; name=\"${formdataEscape(normalizeLinefeeds(name))}\"` +\n          `\\r\\n\\r\\n${normalizeLinefeeds(value)}\\r\\n`)\n        blobParts.push(chunk)\n        length += chunk.byteLength\n      } else {\n        const chunk = textEncoder.encode(`${prefix}; name=\"${formdataEscape(normalizeLinefeeds(name))}\"` +\n          (value.name ? `; filename=\"${formdataEscape(value.name)}\"` : '') + '\\r\\n' +\n          `Content-Type: ${\n            value.type || 'application/octet-stream'\n          }\\r\\n\\r\\n`)\n        blobParts.push(chunk, value, rn)\n        if (typeof value.size === 'number') {\n          length += chunk.byteLength + value.size + rn.byteLength\n        } else {\n          hasUnknownSizeValue = true\n        }\n      }\n    }\n\n    // CRLF is appended to the body to function with legacy servers and match other implementations.\n    // https://github.com/curl/curl/blob/3434c6b46e682452973972e8313613dfa58cd690/lib/mime.c#L1029-L1030\n    // https://github.com/form-data/form-data/issues/63\n    const chunk = textEncoder.encode(`--${boundary}--\\r\\n`)\n    blobParts.push(chunk)\n    length += chunk.byteLength\n    if (hasUnknownSizeValue) {\n      length = null\n    }\n\n    // Set source to object.\n    source = object\n\n    action = async function * () {\n      for (const part of blobParts) {\n        if (part.stream) {\n          yield * part.stream()\n        } else {\n          yield part\n        }\n      }\n    }\n\n    // Set type to `multipart/form-data; boundary=`,\n    // followed by the multipart/form-data boundary string generated\n    // by the multipart/form-data encoding algorithm.\n    type = `multipart/form-data; boundary=${boundary}`\n  } else if (webidl.is.Blob(object)) {\n    // Blob\n\n    // Set source to object.\n    source = object\n\n    // Set length to object’s size.\n    length = object.size\n\n    // If object’s type attribute is not the empty byte sequence, set\n    // type to its value.\n    if (object.type) {\n      type = object.type\n    }\n  } else if (typeof object[Symbol.asyncIterator] === 'function') {\n    // If keepalive is true, then throw a TypeError.\n    if (keepalive) {\n      throw new TypeError('keepalive')\n    }\n\n    // If object is disturbed or locked, then throw a TypeError.\n    if (util.isDisturbed(object) || object.locked) {\n      throw new TypeError(\n        'Response body object should not be disturbed or locked'\n      )\n    }\n\n    stream =\n      webidl.is.ReadableStream(object) ? object : ReadableStreamFrom(object)\n  }\n\n  // 11. If source is a byte sequence, then set action to a\n  // step that returns source and length to source’s length.\n  if (typeof source === 'string' || isUint8Array(source)) {\n    action = () => {\n      length = typeof source === 'string' ? Buffer.byteLength(source) : source.length\n      return source\n    }\n  }\n\n  // 12. If action is non-null, then run these steps in parallel:\n  if (action != null) {\n    ;(async () => {\n      // 1. Run action.\n      const result = action()\n\n      // 2. Whenever one or more bytes are available and stream is not errored,\n      //    enqueue the result of creating a Uint8Array from the available bytes into stream.\n      const iterator = result?.[Symbol.asyncIterator]?.()\n      if (iterator) {\n        for await (const bytes of iterator) {\n          if (isErrored(stream)) break\n          if (bytes.length) {\n            controller.enqueue(new Uint8Array(bytes))\n          }\n        }\n      } else if (result?.length && !isErrored(stream)) {\n        controller.enqueue(typeof result === 'string' ? textEncoder.encode(result) : new Uint8Array(result))\n      }\n\n      // 3. When running action is done, close stream.\n      queueMicrotask(() => readableStreamClose(controller))\n    })()\n  }\n\n  // 13. Let body be a body whose stream is stream, source is source,\n  // and length is length.\n  const body = { stream, source, length }\n\n  // 14. Return (body, type).\n  return [body, type]\n}\n\n/**\n * @typedef {object} ExtractBodyResult\n * @property {ReadableStream<Uint8Array<ArrayBuffer>>} stream - The ReadableStream containing the body data\n * @property {any} source - The original source of the body data\n * @property {number | null} length - The length of the body data, or null\n */\n\n/**\n * Safely extract a body with type from a byte sequence or BodyInit object.\n *\n * @param {import('../../../types').BodyInit} object - The BodyInit object to extract from\n * @param {boolean} [keepalive=false] - If true, indicates that the body\n * @returns {[ExtractBodyResult, string | null]} - Returns a tuple containing the body and its type\n *\n * @see https://fetch.spec.whatwg.org/#bodyinit-safely-extract\n */\nfunction safelyExtractBody (object, keepalive = false) {\n  // To safely extract a body and a `Content-Type` value from\n  // a byte sequence or BodyInit object object, run these steps:\n\n  // 1. If object is a ReadableStream object, then:\n  if (webidl.is.ReadableStream(object)) {\n    // Assert: object is neither disturbed nor locked.\n    assert(!util.isDisturbed(object), 'The body has already been consumed.')\n    assert(!object.locked, 'The stream is locked.')\n  }\n\n  // 2. Return the results of extracting object.\n  return extractBody(object, keepalive)\n}\n\nfunction cloneBody (body) {\n  // To clone a body body, run these steps:\n\n  // https://fetch.spec.whatwg.org/#concept-body-clone\n\n  // 1. Let « out1, out2 » be the result of teeing body’s stream.\n  const { 0: out1, 1: out2 } = body.stream.tee()\n\n  // 2. Set body’s stream to out1.\n  body.stream = out1\n\n  // 3. Return a body whose stream is out2 and other members are copied from body.\n  return {\n    stream: out2,\n    length: body.length,\n    source: body.source\n  }\n}\n\nfunction bodyMixinMethods (instance, getInternalState) {\n  const methods = {\n    blob () {\n      // The blob() method steps are to return the result of\n      // running consume body with this and the following step\n      // given a byte sequence bytes: return a Blob whose\n      // contents are bytes and whose type attribute is this’s\n      // MIME type.\n      return consumeBody(this, (bytes) => {\n        let mimeType = bodyMimeType(getInternalState(this))\n\n        if (mimeType === null) {\n          mimeType = ''\n        } else if (mimeType) {\n          mimeType = serializeAMimeType(mimeType)\n        }\n\n        // Return a Blob whose contents are bytes and type attribute\n        // is mimeType.\n        return new Blob([bytes], { type: mimeType })\n      }, instance, getInternalState)\n    },\n\n    arrayBuffer () {\n      // The arrayBuffer() method steps are to return the result\n      // of running consume body with this and the following step\n      // given a byte sequence bytes: return a new ArrayBuffer\n      // whose contents are bytes.\n      return consumeBody(this, (bytes) => {\n        return new Uint8Array(bytes).buffer\n      }, instance, getInternalState)\n    },\n\n    text () {\n      // The text() method steps are to return the result of running\n      // consume body with this and UTF-8 decode.\n      return consumeBody(this, utf8DecodeBytes, instance, getInternalState)\n    },\n\n    json () {\n      // The json() method steps are to return the result of running\n      // consume body with this and parse JSON from bytes.\n      return consumeBody(this, parseJSONFromBytes, instance, getInternalState)\n    },\n\n    formData () {\n      // The formData() method steps are to return the result of running\n      // consume body with this and the following step given a byte sequence bytes:\n      return consumeBody(this, (value) => {\n        // 1. Let mimeType be the result of get the MIME type with this.\n        const mimeType = bodyMimeType(getInternalState(this))\n\n        // 2. If mimeType is non-null, then switch on mimeType’s essence and run\n        //    the corresponding steps:\n        if (mimeType !== null) {\n          switch (mimeType.essence) {\n            case 'multipart/form-data': {\n              // 1. ... [long step]\n              // 2. If that fails for some reason, then throw a TypeError.\n              const parsed = multipartFormDataParser(value, mimeType)\n\n              // 3. Return a new FormData object, appending each entry,\n              //    resulting from the parsing operation, to its entry list.\n              const fd = new FormData()\n              setFormDataState(fd, parsed)\n\n              return fd\n            }\n            case 'application/x-www-form-urlencoded': {\n              // 1. Let entries be the result of parsing bytes.\n              const entries = new URLSearchParams(value.toString())\n\n              // 2. If entries is failure, then throw a TypeError.\n\n              // 3. Return a new FormData object whose entry list is entries.\n              const fd = new FormData()\n\n              for (const [name, value] of entries) {\n                fd.append(name, value)\n              }\n\n              return fd\n            }\n          }\n        }\n\n        // 3. Throw a TypeError.\n        throw new TypeError(\n          'Content-Type was not one of \"multipart/form-data\" or \"application/x-www-form-urlencoded\".'\n        )\n      }, instance, getInternalState)\n    },\n\n    bytes () {\n      // The bytes() method steps are to return the result of running consume body\n      // with this and the following step given a byte sequence bytes: return the\n      // result of creating a Uint8Array from bytes in this’s relevant realm.\n      return consumeBody(this, (bytes) => {\n        return new Uint8Array(bytes)\n      }, instance, getInternalState)\n    }\n  }\n\n  return methods\n}\n\nfunction mixinBody (prototype, getInternalState) {\n  Object.assign(prototype.prototype, bodyMixinMethods(prototype, getInternalState))\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-body-consume-body\n * @param {any} object internal state\n * @param {(value: unknown) => unknown} convertBytesToJSValue\n * @param {any} instance\n * @param {(target: any) => any} getInternalState\n */\nfunction consumeBody (object, convertBytesToJSValue, instance, getInternalState) {\n  try {\n    webidl.brandCheck(object, instance)\n  } catch (e) {\n    return Promise.reject(e)\n  }\n\n  object = getInternalState(object)\n\n  // 1. If object is unusable, then return a promise rejected\n  //    with a TypeError.\n  if (bodyUnusable(object)) {\n    return Promise.reject(new TypeError('Body is unusable: Body has already been read'))\n  }\n\n  // 2. Let promise be a new promise.\n  const promise = createDeferredPromise()\n\n  // 3. Let errorSteps given error be to reject promise with error.\n  const errorSteps = promise.reject\n\n  // 4. Let successSteps given a byte sequence data be to resolve\n  //    promise with the result of running convertBytesToJSValue\n  //    with data. If that threw an exception, then run errorSteps\n  //    with that exception.\n  const successSteps = (data) => {\n    try {\n      promise.resolve(convertBytesToJSValue(data))\n    } catch (e) {\n      errorSteps(e)\n    }\n  }\n\n  // 5. If object’s body is null, then run successSteps with an\n  //    empty byte sequence.\n  if (object.body == null) {\n    successSteps(Buffer.allocUnsafe(0))\n    return promise.promise\n  }\n\n  // 6. Otherwise, fully read object’s body given successSteps,\n  //    errorSteps, and object’s relevant global object.\n  fullyReadBody(object.body, successSteps, errorSteps)\n\n  // 7. Return promise.\n  return promise.promise\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#body-unusable\n * @param {any} object internal state\n */\nfunction bodyUnusable (object) {\n  const body = object.body\n\n  // An object including the Body interface mixin is\n  // said to be unusable if its body is non-null and\n  // its body’s stream is disturbed or locked.\n  return body != null && (body.stream.locked || util.isDisturbed(body.stream))\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-body-mime-type\n * @param {any} requestOrResponse internal state\n */\nfunction bodyMimeType (requestOrResponse) {\n  // 1. Let headers be null.\n  // 2. If requestOrResponse is a Request object, then set headers to requestOrResponse’s request’s header list.\n  // 3. Otherwise, set headers to requestOrResponse’s response’s header list.\n  /** @type {import('./headers').HeadersList} */\n  const headers = requestOrResponse.headersList\n\n  // 4. Let mimeType be the result of extracting a MIME type from headers.\n  const mimeType = extractMimeType(headers)\n\n  // 5. If mimeType is failure, then return null.\n  if (mimeType === 'failure') {\n    return null\n  }\n\n  // 6. Return mimeType.\n  return mimeType\n}\n\nmodule.exports = {\n  extractBody,\n  safelyExtractBody,\n  cloneBody,\n  mixinBody,\n  streamRegistry,\n  bodyUnusable\n}\n"
  },
  {
    "path": "lib/web/fetch/constants.js",
    "content": "'use strict'\n\nconst corsSafeListedMethods = /** @type {const} */ (['GET', 'HEAD', 'POST'])\nconst corsSafeListedMethodsSet = new Set(corsSafeListedMethods)\n\nconst nullBodyStatus = /** @type {const} */ ([101, 204, 205, 304])\n\nconst redirectStatus = /** @type {const} */ ([301, 302, 303, 307, 308])\nconst redirectStatusSet = new Set(redirectStatus)\n\n/**\n * @see https://fetch.spec.whatwg.org/#block-bad-port\n */\nconst badPorts = /** @type {const} */ ([\n  '1', '7', '9', '11', '13', '15', '17', '19', '20', '21', '22', '23', '25', '37', '42', '43', '53', '69', '77', '79',\n  '87', '95', '101', '102', '103', '104', '109', '110', '111', '113', '115', '117', '119', '123', '135', '137',\n  '139', '143', '161', '179', '389', '427', '465', '512', '513', '514', '515', '526', '530', '531', '532',\n  '540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723',\n  '2049', '3659', '4045', '4190', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6679',\n  '6697', '10080'\n])\nconst badPortsSet = new Set(badPorts)\n\n/**\n * @see https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header\n */\nconst referrerPolicyTokens = /** @type {const} */ ([\n  'no-referrer',\n  'no-referrer-when-downgrade',\n  'same-origin',\n  'origin',\n  'strict-origin',\n  'origin-when-cross-origin',\n  'strict-origin-when-cross-origin',\n  'unsafe-url'\n])\n\n/**\n * @see https://w3c.github.io/webappsec-referrer-policy/#referrer-policies\n */\nconst referrerPolicy = /** @type {const} */ ([\n  '',\n  ...referrerPolicyTokens\n])\nconst referrerPolicyTokensSet = new Set(referrerPolicyTokens)\n\nconst requestRedirect = /** @type {const} */ (['follow', 'manual', 'error'])\n\nconst safeMethods = /** @type {const} */ (['GET', 'HEAD', 'OPTIONS', 'TRACE'])\nconst safeMethodsSet = new Set(safeMethods)\n\nconst requestMode = /** @type {const} */ (['navigate', 'same-origin', 'no-cors', 'cors'])\n\nconst requestCredentials = /** @type {const} */ (['omit', 'same-origin', 'include'])\n\nconst requestCache = /** @type {const} */ ([\n  'default',\n  'no-store',\n  'reload',\n  'no-cache',\n  'force-cache',\n  'only-if-cached'\n])\n\n/**\n * @see https://fetch.spec.whatwg.org/#request-body-header-name\n */\nconst requestBodyHeader = /** @type {const} */ ([\n  'content-encoding',\n  'content-language',\n  'content-location',\n  'content-type',\n  // See https://github.com/nodejs/undici/issues/2021\n  // 'Content-Length' is a forbidden header name, which is typically\n  // removed in the Headers implementation. However, undici doesn't\n  // filter out headers, so we add it here.\n  'content-length'\n])\n\n/**\n * @see https://fetch.spec.whatwg.org/#enumdef-requestduplex\n */\nconst requestDuplex = /** @type {const} */ ([\n  'half'\n])\n\n/**\n * @see http://fetch.spec.whatwg.org/#forbidden-method\n */\nconst forbiddenMethods = /** @type {const} */ (['CONNECT', 'TRACE', 'TRACK'])\nconst forbiddenMethodsSet = new Set(forbiddenMethods)\n\nconst subresource = /** @type {const} */ ([\n  'audio',\n  'audioworklet',\n  'font',\n  'image',\n  'manifest',\n  'paintworklet',\n  'script',\n  'style',\n  'track',\n  'video',\n  'xslt',\n  ''\n])\nconst subresourceSet = new Set(subresource)\n\nmodule.exports = {\n  subresource,\n  forbiddenMethods,\n  requestBodyHeader,\n  referrerPolicy,\n  requestRedirect,\n  requestMode,\n  requestCredentials,\n  requestCache,\n  redirectStatus,\n  corsSafeListedMethods,\n  nullBodyStatus,\n  safeMethods,\n  badPorts,\n  requestDuplex,\n  subresourceSet,\n  badPortsSet,\n  redirectStatusSet,\n  corsSafeListedMethodsSet,\n  safeMethodsSet,\n  forbiddenMethodsSet,\n  referrerPolicyTokens: referrerPolicyTokensSet\n}\n"
  },
  {
    "path": "lib/web/fetch/data-url.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { forgivingBase64, collectASequenceOfCodePoints, collectASequenceOfCodePointsFast, isomorphicDecode, removeASCIIWhitespace, removeChars } = require('../infra')\n\nconst encoder = new TextEncoder()\n\n/**\n * @see https://mimesniff.spec.whatwg.org/#http-token-code-point\n */\nconst HTTP_TOKEN_CODEPOINTS = /^[-!#$%&'*+.^_|~A-Za-z0-9]+$/u\nconst HTTP_WHITESPACE_REGEX = /[\\u000A\\u000D\\u0009\\u0020]/u // eslint-disable-line\n\n/**\n * @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point\n */\nconst HTTP_QUOTED_STRING_TOKENS = /^[\\u0009\\u0020-\\u007E\\u0080-\\u00FF]+$/u // eslint-disable-line\n\n// https://fetch.spec.whatwg.org/#data-url-processor\n/** @param {URL} dataURL */\nfunction dataURLProcessor (dataURL) {\n  // 1. Assert: dataURL’s scheme is \"data\".\n  assert(dataURL.protocol === 'data:')\n\n  // 2. Let input be the result of running the URL\n  // serializer on dataURL with exclude fragment\n  // set to true.\n  let input = URLSerializer(dataURL, true)\n\n  // 3. Remove the leading \"data:\" string from input.\n  input = input.slice(5)\n\n  // 4. Let position point at the start of input.\n  const position = { position: 0 }\n\n  // 5. Let mimeType be the result of collecting a\n  // sequence of code points that are not equal\n  // to U+002C (,), given position.\n  let mimeType = collectASequenceOfCodePointsFast(\n    ',',\n    input,\n    position\n  )\n\n  // 6. Strip leading and trailing ASCII whitespace\n  // from mimeType.\n  // Undici implementation note: we need to store the\n  // length because if the mimetype has spaces removed,\n  // the wrong amount will be sliced from the input in\n  // step #9\n  const mimeTypeLength = mimeType.length\n  mimeType = removeASCIIWhitespace(mimeType, true, true)\n\n  // 7. If position is past the end of input, then\n  // return failure\n  if (position.position >= input.length) {\n    return 'failure'\n  }\n\n  // 8. Advance position by 1.\n  position.position++\n\n  // 9. Let encodedBody be the remainder of input.\n  const encodedBody = input.slice(mimeTypeLength + 1)\n\n  // 10. Let body be the percent-decoding of encodedBody.\n  let body = stringPercentDecode(encodedBody)\n\n  // 11. If mimeType ends with U+003B (;), followed by\n  // zero or more U+0020 SPACE, followed by an ASCII\n  // case-insensitive match for \"base64\", then:\n  if (/;(?:\\u0020*)base64$/ui.test(mimeType)) {\n    // 1. Let stringBody be the isomorphic decode of body.\n    const stringBody = isomorphicDecode(body)\n\n    // 2. Set body to the forgiving-base64 decode of\n    // stringBody.\n    body = forgivingBase64(stringBody)\n\n    // 3. If body is failure, then return failure.\n    if (body === 'failure') {\n      return 'failure'\n    }\n\n    // 4. Remove the last 6 code points from mimeType.\n    mimeType = mimeType.slice(0, -6)\n\n    // 5. Remove trailing U+0020 SPACE code points from mimeType,\n    // if any.\n    mimeType = mimeType.replace(/(\\u0020+)$/u, '')\n\n    // 6. Remove the last U+003B (;) code point from mimeType.\n    mimeType = mimeType.slice(0, -1)\n  }\n\n  // 12. If mimeType starts with U+003B (;), then prepend\n  // \"text/plain\" to mimeType.\n  if (mimeType.startsWith(';')) {\n    mimeType = 'text/plain' + mimeType\n  }\n\n  // 13. Let mimeTypeRecord be the result of parsing\n  // mimeType.\n  let mimeTypeRecord = parseMIMEType(mimeType)\n\n  // 14. If mimeTypeRecord is failure, then set\n  // mimeTypeRecord to text/plain;charset=US-ASCII.\n  if (mimeTypeRecord === 'failure') {\n    mimeTypeRecord = parseMIMEType('text/plain;charset=US-ASCII')\n  }\n\n  // 15. Return a new data: URL struct whose MIME\n  // type is mimeTypeRecord and body is body.\n  // https://fetch.spec.whatwg.org/#data-url-struct\n  return { mimeType: mimeTypeRecord, body }\n}\n\n// https://url.spec.whatwg.org/#concept-url-serializer\n/**\n * @param {URL} url\n * @param {boolean} excludeFragment\n */\nfunction URLSerializer (url, excludeFragment = false) {\n  if (!excludeFragment) {\n    return url.href\n  }\n\n  const href = url.href\n  const hashLength = url.hash.length\n\n  const serialized = hashLength === 0 ? href : href.substring(0, href.length - hashLength)\n\n  if (!hashLength && href.endsWith('#')) {\n    return serialized.slice(0, -1)\n  }\n\n  return serialized\n}\n\n// https://url.spec.whatwg.org/#string-percent-decode\n/** @param {string} input */\nfunction stringPercentDecode (input) {\n  // 1. Let bytes be the UTF-8 encoding of input.\n  const bytes = encoder.encode(input)\n\n  // 2. Return the percent-decoding of bytes.\n  return percentDecode(bytes)\n}\n\n/**\n * @param {number} byte\n */\nfunction isHexCharByte (byte) {\n  // 0-9 A-F a-f\n  return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) || (byte >= 0x61 && byte <= 0x66)\n}\n\n/**\n * @param {number} byte\n */\nfunction hexByteToNumber (byte) {\n  return (\n    // 0-9\n    byte >= 0x30 && byte <= 0x39\n      ? (byte - 48)\n    // Convert to uppercase\n    // ((byte & 0xDF) - 65) + 10\n      : ((byte & 0xDF) - 55)\n  )\n}\n\n// https://url.spec.whatwg.org/#percent-decode\n/** @param {Uint8Array} input */\nfunction percentDecode (input) {\n  const length = input.length\n  // 1. Let output be an empty byte sequence.\n  /** @type {Uint8Array} */\n  const output = new Uint8Array(length)\n  let j = 0\n  let i = 0\n  // 2. For each byte byte in input:\n  while (i < length) {\n    const byte = input[i]\n\n    // 1. If byte is not 0x25 (%), then append byte to output.\n    if (byte !== 0x25) {\n      output[j++] = byte\n\n    // 2. Otherwise, if byte is 0x25 (%) and the next two bytes\n    // after byte in input are not in the ranges\n    // 0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F),\n    // and 0x61 (a) to 0x66 (f), all inclusive, append byte\n    // to output.\n    } else if (\n      byte === 0x25 &&\n      !(isHexCharByte(input[i + 1]) && isHexCharByte(input[i + 2]))\n    ) {\n      output[j++] = 0x25\n\n    // 3. Otherwise:\n    } else {\n      // 1. Let bytePoint be the two bytes after byte in input,\n      // decoded, and then interpreted as hexadecimal number.\n      // 2. Append a byte whose value is bytePoint to output.\n      output[j++] = (hexByteToNumber(input[i + 1]) << 4) | hexByteToNumber(input[i + 2])\n\n      // 3. Skip the next two bytes in input.\n      i += 2\n    }\n    ++i\n  }\n\n  // 3. Return output.\n  return length === j ? output : output.subarray(0, j)\n}\n\n// https://mimesniff.spec.whatwg.org/#parse-a-mime-type\n/** @param {string} input */\nfunction parseMIMEType (input) {\n  // 1. Remove any leading and trailing HTTP whitespace\n  // from input.\n  input = removeHTTPWhitespace(input, true, true)\n\n  // 2. Let position be a position variable for input,\n  // initially pointing at the start of input.\n  const position = { position: 0 }\n\n  // 3. Let type be the result of collecting a sequence\n  // of code points that are not U+002F (/) from\n  // input, given position.\n  const type = collectASequenceOfCodePointsFast(\n    '/',\n    input,\n    position\n  )\n\n  // 4. If type is the empty string or does not solely\n  // contain HTTP token code points, then return failure.\n  // https://mimesniff.spec.whatwg.org/#http-token-code-point\n  if (type.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(type)) {\n    return 'failure'\n  }\n\n  // 5. If position is past the end of input, then return\n  // failure\n  if (position.position >= input.length) {\n    return 'failure'\n  }\n\n  // 6. Advance position by 1. (This skips past U+002F (/).)\n  position.position++\n\n  // 7. Let subtype be the result of collecting a sequence of\n  // code points that are not U+003B (;) from input, given\n  // position.\n  let subtype = collectASequenceOfCodePointsFast(\n    ';',\n    input,\n    position\n  )\n\n  // 8. Remove any trailing HTTP whitespace from subtype.\n  subtype = removeHTTPWhitespace(subtype, false, true)\n\n  // 9. If subtype is the empty string or does not solely\n  // contain HTTP token code points, then return failure.\n  if (subtype.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(subtype)) {\n    return 'failure'\n  }\n\n  const typeLowercase = type.toLowerCase()\n  const subtypeLowercase = subtype.toLowerCase()\n\n  // 10. Let mimeType be a new MIME type record whose type\n  // is type, in ASCII lowercase, and subtype is subtype,\n  // in ASCII lowercase.\n  // https://mimesniff.spec.whatwg.org/#mime-type\n  const mimeType = {\n    type: typeLowercase,\n    subtype: subtypeLowercase,\n    /** @type {Map<string, string>} */\n    parameters: new Map(),\n    // https://mimesniff.spec.whatwg.org/#mime-type-essence\n    essence: `${typeLowercase}/${subtypeLowercase}`\n  }\n\n  // 11. While position is not past the end of input:\n  while (position.position < input.length) {\n    // 1. Advance position by 1. (This skips past U+003B (;).)\n    position.position++\n\n    // 2. Collect a sequence of code points that are HTTP\n    // whitespace from input given position.\n    collectASequenceOfCodePoints(\n      // https://fetch.spec.whatwg.org/#http-whitespace\n      char => HTTP_WHITESPACE_REGEX.test(char),\n      input,\n      position\n    )\n\n    // 3. Let parameterName be the result of collecting a\n    // sequence of code points that are not U+003B (;)\n    // or U+003D (=) from input, given position.\n    let parameterName = collectASequenceOfCodePoints(\n      (char) => char !== ';' && char !== '=',\n      input,\n      position\n    )\n\n    // 4. Set parameterName to parameterName, in ASCII\n    // lowercase.\n    parameterName = parameterName.toLowerCase()\n\n    // 5. If position is not past the end of input, then:\n    if (position.position < input.length) {\n      // 1. If the code point at position within input is\n      // U+003B (;), then continue.\n      if (input[position.position] === ';') {\n        continue\n      }\n\n      // 2. Advance position by 1. (This skips past U+003D (=).)\n      position.position++\n    }\n\n    // 6. If position is past the end of input, then break.\n    if (position.position >= input.length) {\n      break\n    }\n\n    // 7. Let parameterValue be null.\n    let parameterValue = null\n\n    // 8. If the code point at position within input is\n    // U+0022 (\"), then:\n    if (input[position.position] === '\"') {\n      // 1. Set parameterValue to the result of collecting\n      // an HTTP quoted string from input, given position\n      // and the extract-value flag.\n      parameterValue = collectAnHTTPQuotedString(input, position, true)\n\n      // 2. Collect a sequence of code points that are not\n      // U+003B (;) from input, given position.\n      collectASequenceOfCodePointsFast(\n        ';',\n        input,\n        position\n      )\n\n    // 9. Otherwise:\n    } else {\n      // 1. Set parameterValue to the result of collecting\n      // a sequence of code points that are not U+003B (;)\n      // from input, given position.\n      parameterValue = collectASequenceOfCodePointsFast(\n        ';',\n        input,\n        position\n      )\n\n      // 2. Remove any trailing HTTP whitespace from parameterValue.\n      parameterValue = removeHTTPWhitespace(parameterValue, false, true)\n\n      // 3. If parameterValue is the empty string, then continue.\n      if (parameterValue.length === 0) {\n        continue\n      }\n    }\n\n    // 10. If all of the following are true\n    // - parameterName is not the empty string\n    // - parameterName solely contains HTTP token code points\n    // - parameterValue solely contains HTTP quoted-string token code points\n    // - mimeType’s parameters[parameterName] does not exist\n    // then set mimeType’s parameters[parameterName] to parameterValue.\n    if (\n      parameterName.length !== 0 &&\n      HTTP_TOKEN_CODEPOINTS.test(parameterName) &&\n      (parameterValue.length === 0 || HTTP_QUOTED_STRING_TOKENS.test(parameterValue)) &&\n      !mimeType.parameters.has(parameterName)\n    ) {\n      mimeType.parameters.set(parameterName, parameterValue)\n    }\n  }\n\n  // 12. Return mimeType.\n  return mimeType\n}\n\n// https://fetch.spec.whatwg.org/#collect-an-http-quoted-string\n// tests: https://fetch.spec.whatwg.org/#example-http-quoted-string\n/**\n * @param {string} input\n * @param {{ position: number }} position\n * @param {boolean} [extractValue=false]\n */\nfunction collectAnHTTPQuotedString (input, position, extractValue = false) {\n  // 1. Let positionStart be position.\n  const positionStart = position.position\n\n  // 2. Let value be the empty string.\n  let value = ''\n\n  // 3. Assert: the code point at position within input\n  // is U+0022 (\").\n  assert(input[position.position] === '\"')\n\n  // 4. Advance position by 1.\n  position.position++\n\n  // 5. While true:\n  while (true) {\n    // 1. Append the result of collecting a sequence of code points\n    // that are not U+0022 (\") or U+005C (\\) from input, given\n    // position, to value.\n    value += collectASequenceOfCodePoints(\n      (char) => char !== '\"' && char !== '\\\\',\n      input,\n      position\n    )\n\n    // 2. If position is past the end of input, then break.\n    if (position.position >= input.length) {\n      break\n    }\n\n    // 3. Let quoteOrBackslash be the code point at position within\n    // input.\n    const quoteOrBackslash = input[position.position]\n\n    // 4. Advance position by 1.\n    position.position++\n\n    // 5. If quoteOrBackslash is U+005C (\\), then:\n    if (quoteOrBackslash === '\\\\') {\n      // 1. If position is past the end of input, then append\n      // U+005C (\\) to value and break.\n      if (position.position >= input.length) {\n        value += '\\\\'\n        break\n      }\n\n      // 2. Append the code point at position within input to value.\n      value += input[position.position]\n\n      // 3. Advance position by 1.\n      position.position++\n\n    // 6. Otherwise:\n    } else {\n      // 1. Assert: quoteOrBackslash is U+0022 (\").\n      assert(quoteOrBackslash === '\"')\n\n      // 2. Break.\n      break\n    }\n  }\n\n  // 6. If the extract-value flag is set, then return value.\n  if (extractValue) {\n    return value\n  }\n\n  // 7. Return the code points from positionStart to position,\n  // inclusive, within input.\n  return input.slice(positionStart, position.position)\n}\n\n/**\n * @see https://mimesniff.spec.whatwg.org/#serialize-a-mime-type\n */\nfunction serializeAMimeType (mimeType) {\n  assert(mimeType !== 'failure')\n  const { parameters, essence } = mimeType\n\n  // 1. Let serialization be the concatenation of mimeType’s\n  //    type, U+002F (/), and mimeType’s subtype.\n  let serialization = essence\n\n  // 2. For each name → value of mimeType’s parameters:\n  for (let [name, value] of parameters.entries()) {\n    // 1. Append U+003B (;) to serialization.\n    serialization += ';'\n\n    // 2. Append name to serialization.\n    serialization += name\n\n    // 3. Append U+003D (=) to serialization.\n    serialization += '='\n\n    // 4. If value does not solely contain HTTP token code\n    //    points or value is the empty string, then:\n    if (!HTTP_TOKEN_CODEPOINTS.test(value)) {\n      // 1. Precede each occurrence of U+0022 (\") or\n      //    U+005C (\\) in value with U+005C (\\).\n      value = value.replace(/[\\\\\"]/ug, '\\\\$&')\n\n      // 2. Prepend U+0022 (\") to value.\n      value = '\"' + value\n\n      // 3. Append U+0022 (\") to value.\n      value += '\"'\n    }\n\n    // 5. Append value to serialization.\n    serialization += value\n  }\n\n  // 3. Return serialization.\n  return serialization\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#http-whitespace\n * @param {number} char\n */\nfunction isHTTPWhiteSpace (char) {\n  // \"\\r\\n\\t \"\n  return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x020\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#http-whitespace\n * @param {string} str\n * @param {boolean} [leading=true]\n * @param {boolean} [trailing=true]\n */\nfunction removeHTTPWhitespace (str, leading = true, trailing = true) {\n  return removeChars(str, leading, trailing, isHTTPWhiteSpace)\n}\n\n/**\n * @see https://mimesniff.spec.whatwg.org/#minimize-a-supported-mime-type\n * @param {Exclude<ReturnType<typeof parseMIMEType>, 'failure'>} mimeType\n */\nfunction minimizeSupportedMimeType (mimeType) {\n  switch (mimeType.essence) {\n    case 'application/ecmascript':\n    case 'application/javascript':\n    case 'application/x-ecmascript':\n    case 'application/x-javascript':\n    case 'text/ecmascript':\n    case 'text/javascript':\n    case 'text/javascript1.0':\n    case 'text/javascript1.1':\n    case 'text/javascript1.2':\n    case 'text/javascript1.3':\n    case 'text/javascript1.4':\n    case 'text/javascript1.5':\n    case 'text/jscript':\n    case 'text/livescript':\n    case 'text/x-ecmascript':\n    case 'text/x-javascript':\n      // 1. If mimeType is a JavaScript MIME type, then return \"text/javascript\".\n      return 'text/javascript'\n    case 'application/json':\n    case 'text/json':\n      // 2. If mimeType is a JSON MIME type, then return \"application/json\".\n      return 'application/json'\n    case 'image/svg+xml':\n      // 3. If mimeType’s essence is \"image/svg+xml\", then return \"image/svg+xml\".\n      return 'image/svg+xml'\n    case 'text/xml':\n    case 'application/xml':\n      // 4. If mimeType is an XML MIME type, then return \"application/xml\".\n      return 'application/xml'\n  }\n\n  // 2. If mimeType is a JSON MIME type, then return \"application/json\".\n  if (mimeType.subtype.endsWith('+json')) {\n    return 'application/json'\n  }\n\n  // 4. If mimeType is an XML MIME type, then return \"application/xml\".\n  if (mimeType.subtype.endsWith('+xml')) {\n    return 'application/xml'\n  }\n\n  // 5. If mimeType is supported by the user agent, then return mimeType’s essence.\n  // Technically, node doesn't support any mimetypes.\n\n  // 6. Return the empty string.\n  return ''\n}\n\nmodule.exports = {\n  dataURLProcessor,\n  URLSerializer,\n  stringPercentDecode,\n  parseMIMEType,\n  collectAnHTTPQuotedString,\n  serializeAMimeType,\n  removeHTTPWhitespace,\n  minimizeSupportedMimeType,\n  HTTP_TOKEN_CODEPOINTS\n}\n"
  },
  {
    "path": "lib/web/fetch/formdata-parser.js",
    "content": "'use strict'\n\nconst { bufferToLowerCasedHeaderName } = require('../../core/util')\nconst { HTTP_TOKEN_CODEPOINTS } = require('./data-url')\nconst { makeEntry } = require('./formdata')\nconst { webidl } = require('../webidl')\nconst assert = require('node:assert')\nconst { isomorphicDecode } = require('../infra')\n\nconst dd = Buffer.from('--')\nconst decoder = new TextDecoder()\nconst decoderIgnoreBOM = new TextDecoder('utf-8', { ignoreBOM: true })\n\n/**\n * @param {string} chars\n */\nfunction isAsciiString (chars) {\n  for (let i = 0; i < chars.length; ++i) {\n    if ((chars.charCodeAt(i) & ~0x7F) !== 0) {\n      return false\n    }\n  }\n  return true\n}\n\n/**\n * @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-boundary\n * @param {string} boundary\n */\nfunction validateBoundary (boundary) {\n  const length = boundary.length\n\n  // - its length is greater or equal to 27 and lesser or equal to 70, and\n  if (length < 27 || length > 70) {\n    return false\n  }\n\n  // - it is composed by bytes in the ranges 0x30 to 0x39, 0x41 to 0x5A, or\n  //   0x61 to 0x7A, inclusive (ASCII alphanumeric), or which are 0x27 ('),\n  //   0x2D (-) or 0x5F (_).\n  for (let i = 0; i < length; ++i) {\n    const cp = boundary.charCodeAt(i)\n\n    if (!(\n      (cp >= 0x30 && cp <= 0x39) ||\n      (cp >= 0x41 && cp <= 0x5a) ||\n      (cp >= 0x61 && cp <= 0x7a) ||\n      cp === 0x27 ||\n      cp === 0x2d ||\n      cp === 0x5f\n    )) {\n      return false\n    }\n  }\n\n  return true\n}\n\n/**\n * @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-parser\n * @param {Buffer} input\n * @param {ReturnType<import('./data-url')['parseMIMEType']>} mimeType\n */\nfunction multipartFormDataParser (input, mimeType) {\n  // 1. Assert: mimeType’s essence is \"multipart/form-data\".\n  assert(mimeType !== 'failure' && mimeType.essence === 'multipart/form-data')\n\n  const boundaryString = mimeType.parameters.get('boundary')\n\n  // 2. If mimeType’s parameters[\"boundary\"] does not exist, return failure.\n  //    Otherwise, let boundary be the result of UTF-8 decoding mimeType’s\n  //    parameters[\"boundary\"].\n  if (boundaryString === undefined) {\n    throw parsingError('missing boundary in content-type header')\n  }\n\n  const boundary = Buffer.from(`--${boundaryString}`, 'utf8')\n\n  // 3. Let entry list be an empty entry list.\n  const entryList = []\n\n  // 4. Let position be a pointer to a byte in input, initially pointing at\n  //    the first byte.\n  const position = { position: 0 }\n\n  // Note: Per RFC 2046 Section 5.1.1, we must ignore anything before the\n  // first boundary delimiter line (preamble). Search for the first boundary.\n  const firstBoundaryIndex = input.indexOf(boundary)\n\n  if (firstBoundaryIndex === -1) {\n    throw parsingError('no boundary found in multipart body')\n  }\n\n  // Start parsing from the first boundary, ignoring any preamble\n  position.position = firstBoundaryIndex\n\n  // 5. While true:\n  while (true) {\n    // 5.1. If position points to a sequence of bytes starting with 0x2D 0x2D\n    //      (`--`) followed by boundary, advance position by 2 + the length of\n    //      boundary. Otherwise, return failure.\n    // Note: boundary is padded with 2 dashes already, no need to add 2.\n    if (input.subarray(position.position, position.position + boundary.length).equals(boundary)) {\n      position.position += boundary.length\n    } else {\n      throw parsingError('expected a value starting with -- and the boundary')\n    }\n\n    // 5.2. If position points to the sequence of bytes 0x2D 0x2D 0x0D 0x0A\n    //      (`--` followed by CR LF) followed by the end of input, return entry list.\n    // Note: Per RFC 2046 Section 5.1.1, we must ignore anything after the\n    // final boundary delimiter (epilogue). Check for -- or --CRLF and return\n    // regardless of what follows.\n    if (bufferStartsWith(input, dd, position)) {\n      // Found closing boundary delimiter (--), ignore any epilogue\n      return entryList\n    }\n\n    // 5.3. If position does not point to a sequence of bytes starting with 0x0D\n    //      0x0A (CR LF), return failure.\n    if (input[position.position] !== 0x0d || input[position.position + 1] !== 0x0a) {\n      throw parsingError('expected CRLF')\n    }\n\n    // 5.4. Advance position by 2. (This skips past the newline.)\n    position.position += 2\n\n    // 5.5. Let name, filename and contentType be the result of parsing\n    //      multipart/form-data headers on input and position, if the result\n    //      is not failure. Otherwise, return failure.\n    const result = parseMultipartFormDataHeaders(input, position)\n\n    let { name, filename, contentType, encoding } = result\n\n    // 5.6. Advance position by 2. (This skips past the empty line that marks\n    //      the end of the headers.)\n    position.position += 2\n\n    // 5.7. Let body be the empty byte sequence.\n    let body\n\n    // 5.8. Body loop: While position is not past the end of input:\n    // TODO: the steps here are completely wrong\n    {\n      const boundaryIndex = input.indexOf(boundary.subarray(2), position.position)\n\n      if (boundaryIndex === -1) {\n        throw parsingError('expected boundary after body')\n      }\n\n      body = input.subarray(position.position, boundaryIndex - 4)\n\n      position.position += body.length\n\n      // Note: position must be advanced by the body's length before being\n      // decoded, otherwise the parsing will fail.\n      if (encoding === 'base64') {\n        body = Buffer.from(body.toString(), 'base64')\n      }\n    }\n\n    // 5.9. If position does not point to a sequence of bytes starting with\n    //      0x0D 0x0A (CR LF), return failure. Otherwise, advance position by 2.\n    if (input[position.position] !== 0x0d || input[position.position + 1] !== 0x0a) {\n      throw parsingError('expected CRLF')\n    } else {\n      position.position += 2\n    }\n\n    // 5.10. If filename is not null:\n    let value\n\n    if (filename !== null) {\n      // 5.10.1. If contentType is null, set contentType to \"text/plain\".\n      contentType ??= 'text/plain'\n\n      // 5.10.2. If contentType is not an ASCII string, set contentType to the empty string.\n\n      // Note: `buffer.isAscii` can be used at zero-cost, but converting a string to a buffer is a high overhead.\n      // Content-Type is a relatively small string, so it is faster to use `String#charCodeAt`.\n      if (!isAsciiString(contentType)) {\n        contentType = ''\n      }\n\n      // 5.10.3. Let value be a new File object with name filename, type contentType, and body body.\n      value = new File([body], filename, { type: contentType })\n    } else {\n      // 5.11. Otherwise:\n\n      // 5.11.1. Let value be the UTF-8 decoding without BOM of body.\n      value = decoderIgnoreBOM.decode(Buffer.from(body))\n    }\n\n    // 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.\n    assert(webidl.is.USVString(name))\n    assert((typeof value === 'string' && webidl.is.USVString(value)) || webidl.is.File(value))\n\n    // 5.13. Create an entry with name and value, and append it to entry list.\n    entryList.push(makeEntry(name, value, filename))\n  }\n}\n\n/**\n * Parses content-disposition attributes (e.g., name=\"value\" or filename*=utf-8''encoded)\n * @param {Buffer} input\n * @param {{ position: number }} position\n * @returns {{ name: string, value: string }}\n */\nfunction parseContentDispositionAttribute (input, position) {\n  // Skip leading semicolon and whitespace\n  if (input[position.position] === 0x3b /* ; */) {\n    position.position++\n  }\n\n  // Skip whitespace\n  collectASequenceOfBytes(\n    (char) => char === 0x20 || char === 0x09,\n    input,\n    position\n  )\n\n  // Collect attribute name (token characters)\n  const attributeName = collectASequenceOfBytes(\n    (char) => isToken(char) && char !== 0x3d && char !== 0x2a, // not = or *\n    input,\n    position\n  )\n\n  if (attributeName.length === 0) {\n    return null\n  }\n\n  const attrNameStr = attributeName.toString('ascii').toLowerCase()\n\n  // Check for extended notation (attribute*)\n  const isExtended = input[position.position] === 0x2a /* * */\n  if (isExtended) {\n    position.position++ // skip *\n  }\n\n  // Expect = sign\n  if (input[position.position] !== 0x3d /* = */) {\n    return null\n  }\n  position.position++ // skip =\n\n  // Skip whitespace\n  collectASequenceOfBytes(\n    (char) => char === 0x20 || char === 0x09,\n    input,\n    position\n  )\n\n  let value\n\n  if (isExtended) {\n    // Extended attribute format: charset'language'encoded-value\n    const headerValue = collectASequenceOfBytes(\n      (char) => char !== 0x20 && char !== 0x0d && char !== 0x0a && char !== 0x3b, // not space, CRLF, or ;\n      input,\n      position\n    )\n\n    // Check for utf-8'' prefix (case insensitive)\n    if (\n      (headerValue[0] !== 0x75 && headerValue[0] !== 0x55) || // u or U\n      (headerValue[1] !== 0x74 && headerValue[1] !== 0x54) || // t or T\n      (headerValue[2] !== 0x66 && headerValue[2] !== 0x46) || // f or F\n      headerValue[3] !== 0x2d || // -\n      headerValue[4] !== 0x38 // 8\n    ) {\n      throw parsingError('unknown encoding, expected utf-8\\'\\'')\n    }\n\n    // Skip utf-8'' and decode the rest\n    value = decodeURIComponent(decoder.decode(headerValue.subarray(7)))\n  } else if (input[position.position] === 0x22 /* \" */) {\n    // Quoted string\n    position.position++ // skip opening quote\n\n    const quotedValue = collectASequenceOfBytes(\n      (char) => char !== 0x0a && char !== 0x0d && char !== 0x22, // not LF, CR, or \"\n      input,\n      position\n    )\n\n    if (input[position.position] !== 0x22) {\n      throw parsingError('Closing quote not found')\n    }\n    position.position++ // skip closing quote\n\n    value = decoder.decode(quotedValue)\n      .replace(/%0A/ig, '\\n')\n      .replace(/%0D/ig, '\\r')\n      .replace(/%22/g, '\"')\n  } else {\n    // Token value (no quotes)\n    const tokenValue = collectASequenceOfBytes(\n      (char) => isToken(char) && char !== 0x3b, // not ;\n      input,\n      position\n    )\n\n    value = decoder.decode(tokenValue)\n  }\n\n  return { name: attrNameStr, value }\n}\n\n/**\n * @see https://andreubotella.github.io/multipart-form-data/#parse-multipart-form-data-headers\n * @param {Buffer} input\n * @param {{ position: number }} position\n */\nfunction parseMultipartFormDataHeaders (input, position) {\n  // 1. Let name, filename and contentType be null.\n  let name = null\n  let filename = null\n  let contentType = null\n  let encoding = null\n\n  // 2. While true:\n  while (true) {\n    // 2.1. If position points to a sequence of bytes starting with 0x0D 0x0A (CR LF):\n    if (input[position.position] === 0x0d && input[position.position + 1] === 0x0a) {\n      // 2.1.1. If name is null, return failure.\n      if (name === null) {\n        throw parsingError('header name is null')\n      }\n\n      // 2.1.2. Return name, filename and contentType.\n      return { name, filename, contentType, encoding }\n    }\n\n    // 2.2. Let header name be the result of collecting a sequence of bytes that are\n    //      not 0x0A (LF), 0x0D (CR) or 0x3A (:), given position.\n    let headerName = collectASequenceOfBytes(\n      (char) => char !== 0x0a && char !== 0x0d && char !== 0x3a,\n      input,\n      position\n    )\n\n    // 2.3. Remove any HTTP tab or space bytes from the start or end of header name.\n    headerName = removeChars(headerName, true, true, (char) => char === 0x9 || char === 0x20)\n\n    // 2.4. If header name does not match the field-name token production, return failure.\n    if (!HTTP_TOKEN_CODEPOINTS.test(headerName.toString())) {\n      throw parsingError('header name does not match the field-name token production')\n    }\n\n    // 2.5. If the byte at position is not 0x3A (:), return failure.\n    if (input[position.position] !== 0x3a) {\n      throw parsingError('expected :')\n    }\n\n    // 2.6. Advance position by 1.\n    position.position++\n\n    // 2.7. Collect a sequence of bytes that are HTTP tab or space bytes given position.\n    //      (Do nothing with those bytes.)\n    collectASequenceOfBytes(\n      (char) => char === 0x20 || char === 0x09,\n      input,\n      position\n    )\n\n    // 2.8. Byte-lowercase header name and switch on the result:\n    switch (bufferToLowerCasedHeaderName(headerName)) {\n      case 'content-disposition': {\n        name = filename = null\n\n        // Collect the disposition type (should be \"form-data\")\n        const dispositionType = collectASequenceOfBytes(\n          (char) => isToken(char),\n          input,\n          position\n        )\n\n        if (dispositionType.toString('ascii').toLowerCase() !== 'form-data') {\n          throw parsingError('expected form-data for content-disposition header')\n        }\n\n        // Parse attributes recursively until CRLF\n        while (\n          position.position < input.length &&\n          input[position.position] !== 0x0d &&\n          input[position.position + 1] !== 0x0a\n        ) {\n          const attribute = parseContentDispositionAttribute(input, position)\n\n          if (!attribute) {\n            break\n          }\n\n          if (attribute.name === 'name') {\n            name = attribute.value\n          } else if (attribute.name === 'filename') {\n            filename = attribute.value\n          }\n        }\n\n        if (name === null) {\n          throw parsingError('name attribute is required in content-disposition header')\n        }\n\n        break\n      }\n      case 'content-type': {\n        // 1. Let header value be the result of collecting a sequence of bytes that are\n        //    not 0x0A (LF) or 0x0D (CR), given position.\n        let headerValue = collectASequenceOfBytes(\n          (char) => char !== 0x0a && char !== 0x0d,\n          input,\n          position\n        )\n\n        // 2. Remove any HTTP tab or space bytes from the end of header value.\n        headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)\n\n        // 3. Set contentType to the isomorphic decoding of header value.\n        contentType = isomorphicDecode(headerValue)\n\n        break\n      }\n      case 'content-transfer-encoding': {\n        let headerValue = collectASequenceOfBytes(\n          (char) => char !== 0x0a && char !== 0x0d,\n          input,\n          position\n        )\n\n        headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)\n\n        encoding = isomorphicDecode(headerValue)\n\n        break\n      }\n      default: {\n        // Collect a sequence of bytes that are not 0x0A (LF) or 0x0D (CR), given position.\n        // (Do nothing with those bytes.)\n        collectASequenceOfBytes(\n          (char) => char !== 0x0a && char !== 0x0d,\n          input,\n          position\n        )\n      }\n    }\n\n    // 2.9. If position does not point to a sequence of bytes starting with 0x0D 0x0A\n    //      (CR LF), return failure. Otherwise, advance position by 2 (past the newline).\n    if (input[position.position] !== 0x0d && input[position.position + 1] !== 0x0a) {\n      throw parsingError('expected CRLF')\n    } else {\n      position.position += 2\n    }\n  }\n}\n\n/**\n * @param {(char: number) => boolean} condition\n * @param {Buffer} input\n * @param {{ position: number }} position\n */\nfunction collectASequenceOfBytes (condition, input, position) {\n  let start = position.position\n\n  while (start < input.length && condition(input[start])) {\n    ++start\n  }\n\n  return input.subarray(position.position, (position.position = start))\n}\n\n/**\n * @param {Buffer} buf\n * @param {boolean} leading\n * @param {boolean} trailing\n * @param {(charCode: number) => boolean} predicate\n * @returns {Buffer}\n */\nfunction removeChars (buf, leading, trailing, predicate) {\n  let lead = 0\n  let trail = buf.length - 1\n\n  if (leading) {\n    while (lead < buf.length && predicate(buf[lead])) lead++\n  }\n\n  if (trailing) {\n    while (trail > 0 && predicate(buf[trail])) trail--\n  }\n\n  return lead === 0 && trail === buf.length - 1 ? buf : buf.subarray(lead, trail + 1)\n}\n\n/**\n * Checks if {@param buffer} starts with {@param start}\n * @param {Buffer} buffer\n * @param {Buffer} start\n * @param {{ position: number }} position\n */\nfunction bufferStartsWith (buffer, start, position) {\n  if (buffer.length < start.length) {\n    return false\n  }\n\n  for (let i = 0; i < start.length; i++) {\n    if (start[i] !== buffer[position.position + i]) {\n      return false\n    }\n  }\n\n  return true\n}\n\nfunction parsingError (cause) {\n  return new TypeError('Failed to parse body as FormData.', { cause: new TypeError(cause) })\n}\n\n/**\n * CTL            = <any US-ASCII control character\n *                  (octets 0 - 31) and DEL (127)>\n * @param {number} char\n */\nfunction isCTL (char) {\n  return char <= 0x1f || char === 0x7f\n}\n\n/**\n * tspecials :=  \"(\" / \")\" / \"<\" / \">\" / \"@\" /\n *                \",\" / \";\" / \":\" / \"\\\" / <\">\n *                \"/\" / \"[\" / \"]\" / \"?\" / \"=\"\n *                ; Must be in quoted-string,\n *                ; to use within parameter values\n * @param {number} char\n */\nfunction isTSpecial (char) {\n  return (\n    char === 0x28 || // (\n    char === 0x29 || // )\n    char === 0x3c || // <\n    char === 0x3e || // >\n    char === 0x40 || // @\n    char === 0x2c || // ,\n    char === 0x3b || // ;\n    char === 0x3a || // :\n    char === 0x5c || // \\\n    char === 0x22 || // \"\n    char === 0x2f || // /\n    char === 0x5b || // [\n    char === 0x5d || // ]\n    char === 0x3f || // ?\n    char === 0x3d    // +\n  )\n}\n\n/**\n * token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,\n *          or tspecials>\n * @param {number} char\n */\nfunction isToken (char) {\n  return (\n    char <= 0x7f &&  // ascii\n    char !== 0x20 && // space\n    char !== 0x09 &&\n    !isCTL(char) &&\n    !isTSpecial(char)\n  )\n}\n\nmodule.exports = {\n  multipartFormDataParser,\n  validateBoundary\n}\n"
  },
  {
    "path": "lib/web/fetch/formdata.js",
    "content": "'use strict'\n\nconst { iteratorMixin } = require('./util')\nconst { kEnumerableProperty } = require('../../core/util')\nconst { webidl } = require('../webidl')\nconst nodeUtil = require('node:util')\n\n// https://xhr.spec.whatwg.org/#formdata\nclass FormData {\n  #state = []\n\n  constructor (form = undefined) {\n    webidl.util.markAsUncloneable(this)\n\n    if (form !== undefined) {\n      throw webidl.errors.conversionFailed({\n        prefix: 'FormData constructor',\n        argument: 'Argument 1',\n        types: ['undefined']\n      })\n    }\n  }\n\n  append (name, value, filename = undefined) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.append'\n    webidl.argumentLengthCheck(arguments, 2, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    if (arguments.length === 3 || webidl.is.Blob(value)) {\n      value = webidl.converters.Blob(value, prefix, 'value')\n\n      if (filename !== undefined) {\n        filename = webidl.converters.USVString(filename)\n      }\n    } else {\n      value = webidl.converters.USVString(value)\n    }\n\n    // 1. Let value be value if given; otherwise blobValue.\n\n    // 2. Let entry be the result of creating an entry with\n    // name, value, and filename if given.\n    const entry = makeEntry(name, value, filename)\n\n    // 3. Append entry to this’s entry list.\n    this.#state.push(entry)\n  }\n\n  delete (name) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.delete'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    // The delete(name) method steps are to remove all entries whose name\n    // is name from this’s entry list.\n    this.#state = this.#state.filter(entry => entry.name !== name)\n  }\n\n  get (name) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.get'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    // 1. If there is no entry whose name is name in this’s entry list,\n    // then return null.\n    const idx = this.#state.findIndex((entry) => entry.name === name)\n    if (idx === -1) {\n      return null\n    }\n\n    // 2. Return the value of the first entry whose name is name from\n    // this’s entry list.\n    return this.#state[idx].value\n  }\n\n  getAll (name) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.getAll'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    // 1. If there is no entry whose name is name in this’s entry list,\n    // then return the empty list.\n    // 2. Return the values of all entries whose name is name, in order,\n    // from this’s entry list.\n    return this.#state\n      .filter((entry) => entry.name === name)\n      .map((entry) => entry.value)\n  }\n\n  has (name) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.has'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    // The has(name) method steps are to return true if there is an entry\n    // whose name is name in this’s entry list; otherwise false.\n    return this.#state.findIndex((entry) => entry.name === name) !== -1\n  }\n\n  set (name, value, filename = undefined) {\n    webidl.brandCheck(this, FormData)\n\n    const prefix = 'FormData.set'\n    webidl.argumentLengthCheck(arguments, 2, prefix)\n\n    name = webidl.converters.USVString(name)\n\n    if (arguments.length === 3 || webidl.is.Blob(value)) {\n      value = webidl.converters.Blob(value, prefix, 'value')\n\n      if (filename !== undefined) {\n        filename = webidl.converters.USVString(filename)\n      }\n    } else {\n      value = webidl.converters.USVString(value)\n    }\n\n    // The set(name, value) and set(name, blobValue, filename) method steps\n    // are:\n\n    // 1. Let value be value if given; otherwise blobValue.\n\n    // 2. Let entry be the result of creating an entry with name, value, and\n    // filename if given.\n    const entry = makeEntry(name, value, filename)\n\n    // 3. If there are entries in this’s entry list whose name is name, then\n    // replace the first such entry with entry and remove the others.\n    const idx = this.#state.findIndex((entry) => entry.name === name)\n    if (idx !== -1) {\n      this.#state = [\n        ...this.#state.slice(0, idx),\n        entry,\n        ...this.#state.slice(idx + 1).filter((entry) => entry.name !== name)\n      ]\n    } else {\n      // 4. Otherwise, append entry to this’s entry list.\n      this.#state.push(entry)\n    }\n  }\n\n  [nodeUtil.inspect.custom] (depth, options) {\n    const state = this.#state.reduce((a, b) => {\n      if (a[b.name]) {\n        if (Array.isArray(a[b.name])) {\n          a[b.name].push(b.value)\n        } else {\n          a[b.name] = [a[b.name], b.value]\n        }\n      } else {\n        a[b.name] = b.value\n      }\n\n      return a\n    }, { __proto__: null })\n\n    options.depth ??= depth\n    options.colors ??= true\n\n    const output = nodeUtil.formatWithOptions(options, state)\n\n    // remove [Object null prototype]\n    return `FormData ${output.slice(output.indexOf(']') + 2)}`\n  }\n\n  /**\n   * @param {FormData} formData\n   */\n  static getFormDataState (formData) {\n    return formData.#state\n  }\n\n  /**\n   * @param {FormData} formData\n   * @param {any[]} newState\n   */\n  static setFormDataState (formData, newState) {\n    formData.#state = newState\n  }\n}\n\nconst { getFormDataState, setFormDataState } = FormData\nReflect.deleteProperty(FormData, 'getFormDataState')\nReflect.deleteProperty(FormData, 'setFormDataState')\n\niteratorMixin('FormData', FormData, getFormDataState, 'name', 'value')\n\nObject.defineProperties(FormData.prototype, {\n  append: kEnumerableProperty,\n  delete: kEnumerableProperty,\n  get: kEnumerableProperty,\n  getAll: kEnumerableProperty,\n  has: kEnumerableProperty,\n  set: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'FormData',\n    configurable: true\n  }\n})\n\n/**\n * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry\n * @param {string} name\n * @param {string|Blob} value\n * @param {?string} filename\n * @returns\n */\nfunction makeEntry (name, value, filename) {\n  // 1. Set name to the result of converting name into a scalar value string.\n  // Note: This operation was done by the webidl converter USVString.\n\n  // 2. If value is a string, then set value to the result of converting\n  //    value into a scalar value string.\n  if (typeof value === 'string') {\n    // Note: This operation was done by the webidl converter USVString.\n  } else {\n    // 3. Otherwise:\n\n    // 1. If value is not a File object, then set value to a new File object,\n    //    representing the same bytes, whose name attribute value is \"blob\"\n    if (!webidl.is.File(value)) {\n      value = new File([value], 'blob', { type: value.type })\n    }\n\n    // 2. If filename is given, then set value to a new File object,\n    //    representing the same bytes, whose name attribute is filename.\n    if (filename !== undefined) {\n      /** @type {FilePropertyBag} */\n      const options = {\n        type: value.type,\n        lastModified: value.lastModified\n      }\n\n      value = new File([value], filename, options)\n    }\n  }\n\n  // 4. Return an entry whose name is name and whose value is value.\n  return { name, value }\n}\n\nwebidl.is.FormData = webidl.util.MakeTypeAssertion(FormData)\n\nmodule.exports = { FormData, makeEntry, setFormDataState }\n"
  },
  {
    "path": "lib/web/fetch/global.js",
    "content": "'use strict'\n\n// In case of breaking changes, increase the version\n// number to avoid conflicts.\nconst globalOrigin = Symbol.for('undici.globalOrigin.1')\n\nfunction getGlobalOrigin () {\n  return globalThis[globalOrigin]\n}\n\nfunction setGlobalOrigin (newOrigin) {\n  if (newOrigin === undefined) {\n    Object.defineProperty(globalThis, globalOrigin, {\n      value: undefined,\n      writable: true,\n      enumerable: false,\n      configurable: false\n    })\n\n    return\n  }\n\n  const parsedURL = new URL(newOrigin)\n\n  if (parsedURL.protocol !== 'http:' && parsedURL.protocol !== 'https:') {\n    throw new TypeError(`Only http & https urls are allowed, received ${parsedURL.protocol}`)\n  }\n\n  Object.defineProperty(globalThis, globalOrigin, {\n    value: parsedURL,\n    writable: true,\n    enumerable: false,\n    configurable: false\n  })\n}\n\nmodule.exports = {\n  getGlobalOrigin,\n  setGlobalOrigin\n}\n"
  },
  {
    "path": "lib/web/fetch/headers.js",
    "content": "// https://github.com/Ethan-Arrowood/undici-fetch\n\n'use strict'\n\nconst { kConstruct } = require('../../core/symbols')\nconst { kEnumerableProperty } = require('../../core/util')\nconst {\n  iteratorMixin,\n  isValidHeaderName,\n  isValidHeaderValue\n} = require('./util')\nconst { webidl } = require('../webidl')\nconst assert = require('node:assert')\nconst util = require('node:util')\n\n/**\n * @param {number} code\n * @returns {code is (0x0a | 0x0d | 0x09 | 0x20)}\n */\nfunction isHTTPWhiteSpaceCharCode (code) {\n  return code === 0x0a || code === 0x0d || code === 0x09 || code === 0x20\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize\n * @param {string} potentialValue\n * @returns {string}\n */\nfunction headerValueNormalize (potentialValue) {\n  //  To normalize a byte sequence potentialValue, remove\n  //  any leading and trailing HTTP whitespace bytes from\n  //  potentialValue.\n  let i = 0; let j = potentialValue.length\n\n  while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1))) --j\n  while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i\n\n  return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j)\n}\n\n/**\n * @param {Headers} headers\n * @param {Array|Object} object\n */\nfunction fill (headers, object) {\n  // To fill a Headers object headers with a given object object, run these steps:\n\n  // 1. If object is a sequence, then for each header in object:\n  // Note: webidl conversion to array has already been done.\n  if (Array.isArray(object)) {\n    for (let i = 0; i < object.length; ++i) {\n      const header = object[i]\n      // 1. If header does not contain exactly two items, then throw a TypeError.\n      if (header.length !== 2) {\n        throw webidl.errors.exception({\n          header: 'Headers constructor',\n          message: `expected name/value pair to be length 2, found ${header.length}.`\n        })\n      }\n\n      // 2. Append (header’s first item, header’s second item) to headers.\n      appendHeader(headers, header[0], header[1])\n    }\n  } else if (typeof object === 'object' && object !== null) {\n    // Note: null should throw\n\n    // 2. Otherwise, object is a record, then for each key → value in object,\n    //    append (key, value) to headers\n    const keys = Object.keys(object)\n    for (let i = 0; i < keys.length; ++i) {\n      appendHeader(headers, keys[i], object[keys[i]])\n    }\n  } else {\n    throw webidl.errors.conversionFailed({\n      prefix: 'Headers constructor',\n      argument: 'Argument 1',\n      types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']\n    })\n  }\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-headers-append\n * @param {Headers} headers\n * @param {string} name\n * @param {string} value\n */\nfunction appendHeader (headers, name, value) {\n  // 1. Normalize value.\n  value = headerValueNormalize(value)\n\n  // 2. If name is not a header name or value is not a\n  //    header value, then throw a TypeError.\n  if (!isValidHeaderName(name)) {\n    throw webidl.errors.invalidArgument({\n      prefix: 'Headers.append',\n      value: name,\n      type: 'header name'\n    })\n  } else if (!isValidHeaderValue(value)) {\n    throw webidl.errors.invalidArgument({\n      prefix: 'Headers.append',\n      value,\n      type: 'header value'\n    })\n  }\n\n  // 3. If headers’s guard is \"immutable\", then throw a TypeError.\n  // 4. Otherwise, if headers’s guard is \"request\" and name is a\n  //    forbidden header name, return.\n  // 5. Otherwise, if headers’s guard is \"request-no-cors\":\n  //    TODO\n  // Note: undici does not implement forbidden header names\n  if (getHeadersGuard(headers) === 'immutable') {\n    throw new TypeError('immutable')\n  }\n\n  // 6. Otherwise, if headers’s guard is \"response\" and name is a\n  //    forbidden response-header name, return.\n\n  // 7. Append (name, value) to headers’s header list.\n  return getHeadersList(headers).append(name, value, false)\n\n  // 8. If headers’s guard is \"request-no-cors\", then remove\n  //    privileged no-CORS request headers from headers\n}\n\n// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine\n/**\n * @param {Headers} target\n */\nfunction headersListSortAndCombine (target) {\n  const headersList = getHeadersList(target)\n\n  if (!headersList) {\n    return []\n  }\n\n  if (headersList.sortedMap) {\n    return headersList.sortedMap\n  }\n\n  // 1. Let headers be an empty list of headers with the key being the name\n  //    and value the value.\n  const headers = []\n\n  // 2. Let names be the result of convert header names to a sorted-lowercase\n  //    set with all the names of the headers in list.\n  const names = headersList.toSortedArray()\n\n  const cookies = headersList.cookies\n\n  // fast-path\n  if (cookies === null || cookies.length === 1) {\n    // Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`\n    return (headersList.sortedMap = names)\n  }\n\n  // 3. For each name of names:\n  for (let i = 0; i < names.length; ++i) {\n    const { 0: name, 1: value } = names[i]\n    // 1. If name is `set-cookie`, then:\n    if (name === 'set-cookie') {\n      // 1. Let values be a list of all values of headers in list whose name\n      //    is a byte-case-insensitive match for name, in order.\n\n      // 2. For each value of values:\n      // 1. Append (name, value) to headers.\n      for (let j = 0; j < cookies.length; ++j) {\n        headers.push([name, cookies[j]])\n      }\n    } else {\n      // 2. Otherwise:\n\n      // 1. Let value be the result of getting name from list.\n\n      // 2. Assert: value is non-null.\n      // Note: This operation was done by `HeadersList#toSortedArray`.\n\n      // 3. Append (name, value) to headers.\n      headers.push([name, value])\n    }\n  }\n\n  // 4. Return headers.\n  return (headersList.sortedMap = headers)\n}\n\nfunction compareHeaderName (a, b) {\n  return a[0] < b[0] ? -1 : 1\n}\n\nclass HeadersList {\n  /** @type {[string, string][]|null} */\n  cookies = null\n\n  sortedMap\n  headersMap\n\n  constructor (init) {\n    if (init instanceof HeadersList) {\n      this.headersMap = new Map(init.headersMap)\n      this.sortedMap = init.sortedMap\n      this.cookies = init.cookies === null ? null : [...init.cookies]\n    } else {\n      this.headersMap = new Map(init)\n      this.sortedMap = null\n    }\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#header-list-contains\n   * @param {string} name\n   * @param {boolean} isLowerCase\n   */\n  contains (name, isLowerCase) {\n    // A header list list contains a header name name if list\n    // contains a header whose name is a byte-case-insensitive\n    // match for name.\n\n    return this.headersMap.has(isLowerCase ? name : name.toLowerCase())\n  }\n\n  clear () {\n    this.headersMap.clear()\n    this.sortedMap = null\n    this.cookies = null\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#concept-header-list-append\n   * @param {string} name\n   * @param {string} value\n   * @param {boolean} isLowerCase\n   */\n  append (name, value, isLowerCase) {\n    this.sortedMap = null\n\n    // 1. If list contains name, then set name to the first such\n    //    header’s name.\n    const lowercaseName = isLowerCase ? name : name.toLowerCase()\n    const exists = this.headersMap.get(lowercaseName)\n\n    // 2. Append (name, value) to list.\n    if (exists) {\n      const delimiter = lowercaseName === 'cookie' ? '; ' : ', '\n      this.headersMap.set(lowercaseName, {\n        name: exists.name,\n        value: `${exists.value}${delimiter}${value}`\n      })\n    } else {\n      this.headersMap.set(lowercaseName, { name, value })\n    }\n\n    if (lowercaseName === 'set-cookie') {\n      (this.cookies ??= []).push(value)\n    }\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#concept-header-list-set\n   * @param {string} name\n   * @param {string} value\n   * @param {boolean} isLowerCase\n   */\n  set (name, value, isLowerCase) {\n    this.sortedMap = null\n    const lowercaseName = isLowerCase ? name : name.toLowerCase()\n\n    if (lowercaseName === 'set-cookie') {\n      this.cookies = [value]\n    }\n\n    // 1. If list contains name, then set the value of\n    //    the first such header to value and remove the\n    //    others.\n    // 2. Otherwise, append header (name, value) to list.\n    this.headersMap.set(lowercaseName, { name, value })\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#concept-header-list-delete\n   * @param {string} name\n   * @param {boolean} isLowerCase\n   */\n  delete (name, isLowerCase) {\n    this.sortedMap = null\n    if (!isLowerCase) name = name.toLowerCase()\n\n    if (name === 'set-cookie') {\n      this.cookies = null\n    }\n\n    this.headersMap.delete(name)\n  }\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#concept-header-list-get\n   * @param {string} name\n   * @param {boolean} isLowerCase\n   * @returns {string | null}\n   */\n  get (name, isLowerCase) {\n    // 1. If list does not contain name, then return null.\n    // 2. Return the values of all headers in list whose name\n    //    is a byte-case-insensitive match for name,\n    //    separated from each other by 0x2C 0x20, in order.\n    return this.headersMap.get(isLowerCase ? name : name.toLowerCase())?.value ?? null\n  }\n\n  * [Symbol.iterator] () {\n    // use the lowercased name\n    for (const { 0: name, 1: { value } } of this.headersMap) {\n      yield [name, value]\n    }\n  }\n\n  get entries () {\n    const headers = {}\n\n    if (this.headersMap.size !== 0) {\n      for (const { name, value } of this.headersMap.values()) {\n        headers[name] = value\n      }\n    }\n\n    return headers\n  }\n\n  rawValues () {\n    return this.headersMap.values()\n  }\n\n  get entriesList () {\n    const headers = []\n\n    if (this.headersMap.size !== 0) {\n      for (const { 0: lowerName, 1: { name, value } } of this.headersMap) {\n        if (lowerName === 'set-cookie') {\n          for (const cookie of this.cookies) {\n            headers.push([name, cookie])\n          }\n        } else {\n          headers.push([name, value])\n        }\n      }\n    }\n\n    return headers\n  }\n\n  // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set\n  toSortedArray () {\n    const size = this.headersMap.size\n    const array = new Array(size)\n    // In most cases, you will use the fast-path.\n    // fast-path: Use binary insertion sort for small arrays.\n    if (size <= 32) {\n      if (size === 0) {\n        // If empty, it is an empty array. To avoid the first index assignment.\n        return array\n      }\n      // Improve performance by unrolling loop and avoiding double-loop.\n      // Double-loop-less version of the binary insertion sort.\n      const iterator = this.headersMap[Symbol.iterator]()\n      const firstValue = iterator.next().value\n      // set [name, value] to first index.\n      array[0] = [firstValue[0], firstValue[1].value]\n      // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine\n      // 3.2.2. Assert: value is non-null.\n      assert(firstValue[1].value !== null)\n      for (\n        let i = 1, j = 0, right = 0, left = 0, pivot = 0, x, value;\n        i < size;\n        ++i\n      ) {\n        // get next value\n        value = iterator.next().value\n        // set [name, value] to current index.\n        x = array[i] = [value[0], value[1].value]\n        // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine\n        // 3.2.2. Assert: value is non-null.\n        assert(x[1] !== null)\n        left = 0\n        right = i\n        // binary search\n        while (left < right) {\n          // middle index\n          pivot = left + ((right - left) >> 1)\n          // compare header name\n          if (array[pivot][0] <= x[0]) {\n            left = pivot + 1\n          } else {\n            right = pivot\n          }\n        }\n        if (i !== pivot) {\n          j = i\n          while (j > left) {\n            array[j] = array[--j]\n          }\n          array[left] = x\n        }\n      }\n      /* c8 ignore next 4 */\n      if (!iterator.next().done) {\n        // This is for debugging and will never be called.\n        throw new TypeError('Unreachable')\n      }\n      return array\n    } else {\n      // This case would be a rare occurrence.\n      // slow-path: fallback\n      let i = 0\n      for (const { 0: name, 1: { value } } of this.headersMap) {\n        array[i++] = [name, value]\n        // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine\n        // 3.2.2. Assert: value is non-null.\n        assert(value !== null)\n      }\n      return array.sort(compareHeaderName)\n    }\n  }\n}\n\n// https://fetch.spec.whatwg.org/#headers-class\nclass Headers {\n  #guard\n  /**\n   * @type {HeadersList}\n   */\n  #headersList\n\n  /**\n   * @param {HeadersInit|Symbol} [init]\n   * @returns\n   */\n  constructor (init = undefined) {\n    webidl.util.markAsUncloneable(this)\n\n    if (init === kConstruct) {\n      return\n    }\n\n    this.#headersList = new HeadersList()\n\n    // The new Headers(init) constructor steps are:\n\n    // 1. Set this’s guard to \"none\".\n    this.#guard = 'none'\n\n    // 2. If init is given, then fill this with init.\n    if (init !== undefined) {\n      init = webidl.converters.HeadersInit(init, 'Headers constructor', 'init')\n      fill(this, init)\n    }\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-append\n  append (name, value) {\n    webidl.brandCheck(this, Headers)\n\n    webidl.argumentLengthCheck(arguments, 2, 'Headers.append')\n\n    const prefix = 'Headers.append'\n    name = webidl.converters.ByteString(name, prefix, 'name')\n    value = webidl.converters.ByteString(value, prefix, 'value')\n\n    return appendHeader(this, name, value)\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-delete\n  delete (name) {\n    webidl.brandCheck(this, Headers)\n\n    webidl.argumentLengthCheck(arguments, 1, 'Headers.delete')\n\n    const prefix = 'Headers.delete'\n    name = webidl.converters.ByteString(name, prefix, 'name')\n\n    // 1. If name is not a header name, then throw a TypeError.\n    if (!isValidHeaderName(name)) {\n      throw webidl.errors.invalidArgument({\n        prefix: 'Headers.delete',\n        value: name,\n        type: 'header name'\n      })\n    }\n\n    // 2. If this’s guard is \"immutable\", then throw a TypeError.\n    // 3. Otherwise, if this’s guard is \"request\" and name is a\n    //    forbidden header name, return.\n    // 4. Otherwise, if this’s guard is \"request-no-cors\", name\n    //    is not a no-CORS-safelisted request-header name, and\n    //    name is not a privileged no-CORS request-header name,\n    //    return.\n    // 5. Otherwise, if this’s guard is \"response\" and name is\n    //    a forbidden response-header name, return.\n    // Note: undici does not implement forbidden header names\n    if (this.#guard === 'immutable') {\n      throw new TypeError('immutable')\n    }\n\n    // 6. If this’s header list does not contain name, then\n    //    return.\n    if (!this.#headersList.contains(name, false)) {\n      return\n    }\n\n    // 7. Delete name from this’s header list.\n    // 8. If this’s guard is \"request-no-cors\", then remove\n    //    privileged no-CORS request headers from this.\n    this.#headersList.delete(name, false)\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-get\n  get (name) {\n    webidl.brandCheck(this, Headers)\n\n    webidl.argumentLengthCheck(arguments, 1, 'Headers.get')\n\n    const prefix = 'Headers.get'\n    name = webidl.converters.ByteString(name, prefix, 'name')\n\n    // 1. If name is not a header name, then throw a TypeError.\n    if (!isValidHeaderName(name)) {\n      throw webidl.errors.invalidArgument({\n        prefix,\n        value: name,\n        type: 'header name'\n      })\n    }\n\n    // 2. Return the result of getting name from this’s header\n    //    list.\n    return this.#headersList.get(name, false)\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-has\n  has (name) {\n    webidl.brandCheck(this, Headers)\n\n    webidl.argumentLengthCheck(arguments, 1, 'Headers.has')\n\n    const prefix = 'Headers.has'\n    name = webidl.converters.ByteString(name, prefix, 'name')\n\n    // 1. If name is not a header name, then throw a TypeError.\n    if (!isValidHeaderName(name)) {\n      throw webidl.errors.invalidArgument({\n        prefix,\n        value: name,\n        type: 'header name'\n      })\n    }\n\n    // 2. Return true if this’s header list contains name;\n    //    otherwise false.\n    return this.#headersList.contains(name, false)\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-set\n  set (name, value) {\n    webidl.brandCheck(this, Headers)\n\n    webidl.argumentLengthCheck(arguments, 2, 'Headers.set')\n\n    const prefix = 'Headers.set'\n    name = webidl.converters.ByteString(name, prefix, 'name')\n    value = webidl.converters.ByteString(value, prefix, 'value')\n\n    // 1. Normalize value.\n    value = headerValueNormalize(value)\n\n    // 2. If name is not a header name or value is not a\n    //    header value, then throw a TypeError.\n    if (!isValidHeaderName(name)) {\n      throw webidl.errors.invalidArgument({\n        prefix,\n        value: name,\n        type: 'header name'\n      })\n    } else if (!isValidHeaderValue(value)) {\n      throw webidl.errors.invalidArgument({\n        prefix,\n        value,\n        type: 'header value'\n      })\n    }\n\n    // 3. If this’s guard is \"immutable\", then throw a TypeError.\n    // 4. Otherwise, if this’s guard is \"request\" and name is a\n    //    forbidden header name, return.\n    // 5. Otherwise, if this’s guard is \"request-no-cors\" and\n    //    name/value is not a no-CORS-safelisted request-header,\n    //    return.\n    // 6. Otherwise, if this’s guard is \"response\" and name is a\n    //    forbidden response-header name, return.\n    // Note: undici does not implement forbidden header names\n    if (this.#guard === 'immutable') {\n      throw new TypeError('immutable')\n    }\n\n    // 7. Set (name, value) in this’s header list.\n    // 8. If this’s guard is \"request-no-cors\", then remove\n    //    privileged no-CORS request headers from this\n    this.#headersList.set(name, value, false)\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-headers-getsetcookie\n  getSetCookie () {\n    webidl.brandCheck(this, Headers)\n\n    // 1. If this’s header list does not contain `Set-Cookie`, then return « ».\n    // 2. Return the values of all headers in this’s header list whose name is\n    //    a byte-case-insensitive match for `Set-Cookie`, in order.\n\n    const list = this.#headersList.cookies\n\n    if (list) {\n      return [...list]\n    }\n\n    return []\n  }\n\n  [util.inspect.custom] (depth, options) {\n    options.depth ??= depth\n\n    return `Headers ${util.formatWithOptions(options, this.#headersList.entries)}`\n  }\n\n  static getHeadersGuard (o) {\n    return o.#guard\n  }\n\n  static setHeadersGuard (o, guard) {\n    o.#guard = guard\n  }\n\n  /**\n   * @param {Headers} o\n   */\n  static getHeadersList (o) {\n    return o.#headersList\n  }\n\n  /**\n   * @param {Headers} target\n   * @param {HeadersList} list\n   */\n  static setHeadersList (target, list) {\n    target.#headersList = list\n  }\n}\n\nconst { getHeadersGuard, setHeadersGuard, getHeadersList, setHeadersList } = Headers\nReflect.deleteProperty(Headers, 'getHeadersGuard')\nReflect.deleteProperty(Headers, 'setHeadersGuard')\nReflect.deleteProperty(Headers, 'getHeadersList')\nReflect.deleteProperty(Headers, 'setHeadersList')\n\niteratorMixin('Headers', Headers, headersListSortAndCombine, 0, 1)\n\nObject.defineProperties(Headers.prototype, {\n  append: kEnumerableProperty,\n  delete: kEnumerableProperty,\n  get: kEnumerableProperty,\n  has: kEnumerableProperty,\n  set: kEnumerableProperty,\n  getSetCookie: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'Headers',\n    configurable: true\n  },\n  [util.inspect.custom]: {\n    enumerable: false\n  }\n})\n\nwebidl.converters.HeadersInit = function (V, prefix, argument) {\n  if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {\n    const iterator = Reflect.get(V, Symbol.iterator)\n\n    // A work-around to ensure we send the properly-cased Headers when V is a Headers object.\n    // Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.\n    if (!util.types.isProxy(V) && iterator === Headers.prototype.entries) { // Headers object\n      try {\n        return getHeadersList(V).entriesList\n      } catch {\n        // fall-through\n      }\n    }\n\n    if (typeof iterator === 'function') {\n      return webidl.converters['sequence<sequence<ByteString>>'](V, prefix, argument, iterator.bind(V))\n    }\n\n    return webidl.converters['record<ByteString, ByteString>'](V, prefix, argument)\n  }\n\n  throw webidl.errors.conversionFailed({\n    prefix: 'Headers constructor',\n    argument: 'Argument 1',\n    types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']\n  })\n}\n\nmodule.exports = {\n  fill,\n  // for test.\n  compareHeaderName,\n  Headers,\n  HeadersList,\n  getHeadersGuard,\n  setHeadersGuard,\n  setHeadersList,\n  getHeadersList\n}\n"
  },
  {
    "path": "lib/web/fetch/index.js",
    "content": "// https://github.com/Ethan-Arrowood/undici-fetch\n\n'use strict'\n\nconst {\n  makeNetworkError,\n  makeAppropriateNetworkError,\n  filterResponse,\n  makeResponse,\n  fromInnerResponse,\n  getResponseState\n} = require('./response')\nconst { HeadersList } = require('./headers')\nconst { Request, cloneRequest, getRequestDispatcher, getRequestState } = require('./request')\nconst zlib = require('node:zlib')\nconst {\n  makePolicyContainer,\n  clonePolicyContainer,\n  requestBadPort,\n  TAOCheck,\n  appendRequestOriginHeader,\n  responseLocationURL,\n  requestCurrentURL,\n  setRequestReferrerPolicyOnRedirect,\n  tryUpgradeRequestToAPotentiallyTrustworthyURL,\n  createOpaqueTimingInfo,\n  appendFetchMetadata,\n  corsCheck,\n  crossOriginResourcePolicyCheck,\n  determineRequestsReferrer,\n  coarsenedSharedCurrentTime,\n  sameOrigin,\n  isCancelled,\n  isAborted,\n  isErrorLike,\n  fullyReadBody,\n  readableStreamClose,\n  urlIsLocal,\n  urlIsHttpHttpsScheme,\n  urlHasHttpsScheme,\n  clampAndCoarsenConnectionTimingInfo,\n  simpleRangeHeaderValue,\n  buildContentRange,\n  createInflate,\n  extractMimeType,\n  hasAuthenticationEntry,\n  includesCredentials,\n  isTraversableNavigable\n} = require('./util')\nconst assert = require('node:assert')\nconst { safelyExtractBody, extractBody } = require('./body')\nconst {\n  redirectStatusSet,\n  nullBodyStatus,\n  safeMethodsSet,\n  requestBodyHeader,\n  subresourceSet\n} = require('./constants')\nconst EE = require('node:events')\nconst { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')\nconst { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')\nconst { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')\nconst { getGlobalDispatcher } = require('../../global')\nconst { webidl } = require('../webidl')\nconst { STATUS_CODES } = require('node:http')\nconst { bytesMatch } = require('../subresource-integrity/subresource-integrity')\nconst { createDeferredPromise } = require('../../util/promise')\nconst { isomorphicEncode } = require('../infra')\nconst { runtimeFeatures } = require('../../util/runtime-features')\n\n// Node.js v23.8.0+ and v22.15.0+ supports Zstandard\nconst hasZstd = runtimeFeatures.has('zstd')\n\nconst GET_OR_HEAD = ['GET', 'HEAD']\n\nconst defaultUserAgent = typeof __UNDICI_IS_NODE__ !== 'undefined' || typeof esbuildDetection !== 'undefined'\n  ? 'node'\n  : 'undici'\n\n/** @type {import('buffer').resolveObjectURL} */\nlet resolveObjectURL\n\nclass Fetch extends EE {\n  constructor (dispatcher) {\n    super()\n\n    this.dispatcher = dispatcher\n    this.connection = null\n    this.dump = false\n    this.state = 'ongoing'\n  }\n\n  terminate (reason) {\n    if (this.state !== 'ongoing') {\n      return\n    }\n\n    this.state = 'terminated'\n    this.connection?.destroy(reason)\n    this.emit('terminated', reason)\n  }\n\n  // https://fetch.spec.whatwg.org/#fetch-controller-abort\n  abort (error) {\n    if (this.state !== 'ongoing') {\n      return\n    }\n\n    // 1. Set controller’s state to \"aborted\".\n    this.state = 'aborted'\n\n    // 2. Let fallbackError be an \"AbortError\" DOMException.\n    // 3. Set error to fallbackError if it is not given.\n    if (!error) {\n      error = new DOMException('The operation was aborted.', 'AbortError')\n    }\n\n    // 4. Let serializedError be StructuredSerialize(error).\n    //    If that threw an exception, catch it, and let\n    //    serializedError be StructuredSerialize(fallbackError).\n\n    // 5. Set controller’s serialized abort reason to serializedError.\n    this.serializedAbortReason = error\n\n    this.connection?.destroy(error)\n    this.emit('terminated', error)\n  }\n}\n\nfunction handleFetchDone (response) {\n  finalizeAndReportTiming(response, 'fetch')\n}\n\n// https://fetch.spec.whatwg.org/#fetch-method\nfunction fetch (input, init = undefined) {\n  webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch')\n\n  // 1. Let p be a new promise.\n  let p = createDeferredPromise()\n\n  // 2. Let requestObject be the result of invoking the initial value of\n  // Request as constructor with input and init as arguments. If this throws\n  // an exception, reject p with it and return p.\n  let requestObject\n\n  try {\n    requestObject = new Request(input, init)\n  } catch (e) {\n    p.reject(e)\n    return p.promise\n  }\n\n  // 3. Let request be requestObject’s request.\n  const request = getRequestState(requestObject)\n\n  // 4. If requestObject’s signal’s aborted flag is set, then:\n  if (requestObject.signal.aborted) {\n    // 1. Abort the fetch() call with p, request, null, and\n    //    requestObject’s signal’s abort reason.\n    abortFetch(p, request, null, requestObject.signal.reason, null)\n\n    // 2. Return p.\n    return p.promise\n  }\n\n  // 5. Let globalObject be request’s client’s global object.\n  const globalObject = request.client.globalObject\n\n  // 6. If globalObject is a ServiceWorkerGlobalScope object, then set\n  // request’s service-workers mode to \"none\".\n  if (globalObject?.constructor?.name === 'ServiceWorkerGlobalScope') {\n    request.serviceWorkers = 'none'\n  }\n\n  // 7. Let responseObject be null.\n  let responseObject = null\n\n  // 8. Let relevantRealm be this’s relevant Realm.\n\n  // 9. Let locallyAborted be false.\n  let locallyAborted = false\n\n  // 10. Let controller be null.\n  let controller = null\n\n  // 11. Add the following abort steps to requestObject’s signal:\n  addAbortListener(\n    requestObject.signal,\n    () => {\n      // 1. Set locallyAborted to true.\n      locallyAborted = true\n\n      // 2. Assert: controller is non-null.\n      assert(controller != null)\n\n      // 3. Abort controller with requestObject’s signal’s abort reason.\n      controller.abort(requestObject.signal.reason)\n\n      const realResponse = responseObject?.deref()\n\n      // 4. Abort the fetch() call with p, request, responseObject,\n      //    and requestObject’s signal’s abort reason.\n      abortFetch(p, request, realResponse, requestObject.signal.reason, controller.controller)\n    }\n  )\n\n  // 12. Let handleFetchDone given response response be to finalize and\n  // report timing with response, globalObject, and \"fetch\".\n  // see function handleFetchDone\n\n  // 13. Set controller to the result of calling fetch given request,\n  // with processResponseEndOfBody set to handleFetchDone, and processResponse\n  // given response being these substeps:\n\n  const processResponse = (response) => {\n    // 1. If locallyAborted is true, terminate these substeps.\n    if (locallyAborted) {\n      return\n    }\n\n    // 2. If response’s aborted flag is set, then:\n    if (response.aborted) {\n      // 1. Let deserializedError be the result of deserialize a serialized\n      //    abort reason given controller’s serialized abort reason and\n      //    relevantRealm.\n\n      // 2. Abort the fetch() call with p, request, responseObject, and\n      //    deserializedError.\n\n      abortFetch(p, request, responseObject, controller.serializedAbortReason, controller.controller)\n      return\n    }\n\n    // 3. If response is a network error, then reject p with a TypeError\n    // and terminate these substeps.\n    if (response.type === 'error') {\n      p.reject(new TypeError('fetch failed', { cause: response.error }))\n      return\n    }\n\n    // 4. Set responseObject to the result of creating a Response object,\n    // given response, \"immutable\", and relevantRealm.\n    responseObject = new WeakRef(fromInnerResponse(response, 'immutable'))\n\n    // 5. Resolve p with responseObject.\n    p.resolve(responseObject.deref())\n    p = null\n  }\n\n  controller = fetching({\n    request,\n    processResponseEndOfBody: handleFetchDone,\n    processResponse,\n    dispatcher: getRequestDispatcher(requestObject), // undici\n    // Keep requestObject alive to prevent its AbortController from being GC'd\n    // See https://github.com/nodejs/undici/issues/4627\n    requestObject\n  })\n\n  // 14. Return p.\n  return p.promise\n}\n\n// https://fetch.spec.whatwg.org/#finalize-and-report-timing\nfunction finalizeAndReportTiming (response, initiatorType = 'other') {\n  // 1. If response is an aborted network error, then return.\n  if (response.type === 'error' && response.aborted) {\n    return\n  }\n\n  // 2. If response’s URL list is null or empty, then return.\n  if (!response.urlList?.length) {\n    return\n  }\n\n  // 3. Let originalURL be response’s URL list[0].\n  const originalURL = response.urlList[0]\n\n  // 4. Let timingInfo be response’s timing info.\n  let timingInfo = response.timingInfo\n\n  // 5. Let cacheState be response’s cache state.\n  let cacheState = response.cacheState\n\n  // 6. If originalURL’s scheme is not an HTTP(S) scheme, then return.\n  if (!urlIsHttpHttpsScheme(originalURL)) {\n    return\n  }\n\n  // 7. If timingInfo is null, then return.\n  if (timingInfo === null) {\n    return\n  }\n\n  // 8. If response’s timing allow passed flag is not set, then:\n  if (!response.timingAllowPassed) {\n    //  1. Set timingInfo to a the result of creating an opaque timing info for timingInfo.\n    timingInfo = createOpaqueTimingInfo({\n      startTime: timingInfo.startTime\n    })\n\n    //  2. Set cacheState to the empty string.\n    cacheState = ''\n  }\n\n  // 9. Set timingInfo’s end time to the coarsened shared current time\n  // given global’s relevant settings object’s cross-origin isolated\n  // capability.\n  // TODO: given global’s relevant settings object’s cross-origin isolated\n  // capability?\n  timingInfo.endTime = coarsenedSharedCurrentTime()\n\n  // 10. Set response’s timing info to timingInfo.\n  response.timingInfo = timingInfo\n\n  // 11. Mark resource timing for timingInfo, originalURL, initiatorType,\n  // global, and cacheState.\n  markResourceTiming(\n    timingInfo,\n    originalURL.href,\n    initiatorType,\n    globalThis,\n    cacheState,\n    '', // bodyType\n    response.status\n  )\n}\n\n// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing\nconst markResourceTiming = performance.markResourceTiming\n\n// https://fetch.spec.whatwg.org/#abort-fetch\nfunction abortFetch (p, request, responseObject, error, controller /* undici-specific */) {\n  // 1. Reject promise with error.\n  if (p) {\n    // We might have already resolved the promise at this stage\n    p.reject(error)\n  }\n\n  // 2. If request’s body is not null and is readable, then cancel request’s\n  // body with error.\n  if (request.body?.stream != null && isReadable(request.body.stream)) {\n    request.body.stream.cancel(error).catch((err) => {\n      if (err.code === 'ERR_INVALID_STATE') {\n        // Node bug?\n        return\n      }\n      throw err\n    })\n  }\n\n  // 3. If responseObject is null, then return.\n  if (responseObject == null) {\n    return\n  }\n\n  // 4. Let response be responseObject’s response.\n  const response = getResponseState(responseObject)\n\n  // 5. If response’s body is not null and is readable, then error response’s\n  // body with error.\n  if (response.body?.stream != null && isReadable(response.body.stream)) {\n    controller.error(error)\n  }\n}\n\n// https://fetch.spec.whatwg.org/#fetching\nfunction fetching ({\n  request,\n  processRequestBodyChunkLength,\n  processRequestEndOfBody,\n  processResponse,\n  processResponseEndOfBody,\n  processResponseConsumeBody,\n  useParallelQueue = false,\n  dispatcher = getGlobalDispatcher(), // undici\n  requestObject = null // Keep alive to prevent AbortController GC, see #4627\n}) {\n  // Ensure that the dispatcher is set accordingly\n  assert(dispatcher)\n\n  // 1. Let taskDestination be null.\n  let taskDestination = null\n\n  // 2. Let crossOriginIsolatedCapability be false.\n  let crossOriginIsolatedCapability = false\n\n  // 3. If request’s client is non-null, then:\n  if (request.client != null) {\n    // 1. Set taskDestination to request’s client’s global object.\n    taskDestination = request.client.globalObject\n\n    // 2. Set crossOriginIsolatedCapability to request’s client’s cross-origin\n    // isolated capability.\n    crossOriginIsolatedCapability =\n      request.client.crossOriginIsolatedCapability\n  }\n\n  // 4. If useParallelQueue is true, then set taskDestination to the result of\n  // starting a new parallel queue.\n  // TODO\n\n  // 5. Let timingInfo be a new fetch timing info whose start time and\n  // post-redirect start time are the coarsened shared current time given\n  // crossOriginIsolatedCapability.\n  const currentTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)\n  const timingInfo = createOpaqueTimingInfo({\n    startTime: currentTime\n  })\n\n  // 6. Let fetchParams be a new fetch params whose\n  // request is request,\n  // timing info is timingInfo,\n  // process request body chunk length is processRequestBodyChunkLength,\n  // process request end-of-body is processRequestEndOfBody,\n  // process response is processResponse,\n  // process response consume body is processResponseConsumeBody,\n  // process response end-of-body is processResponseEndOfBody,\n  // task destination is taskDestination,\n  // and cross-origin isolated capability is crossOriginIsolatedCapability.\n  const fetchParams = {\n    controller: new Fetch(dispatcher),\n    request,\n    timingInfo,\n    processRequestBodyChunkLength,\n    processRequestEndOfBody,\n    processResponse,\n    processResponseConsumeBody,\n    processResponseEndOfBody,\n    taskDestination,\n    crossOriginIsolatedCapability,\n    // Keep requestObject alive to prevent its AbortController from being GC'd\n    requestObject\n  }\n\n  // 7. If request’s body is a byte sequence, then set request’s body to\n  //    request’s body as a body.\n  // NOTE: Since fetching is only called from fetch, body should already be\n  // extracted.\n  assert(!request.body || request.body.stream)\n\n  // 8. If request’s window is \"client\", then set request’s window to request’s\n  // client, if request’s client’s global object is a Window object; otherwise\n  // \"no-window\".\n  if (request.window === 'client') {\n    // TODO: What if request.client is null?\n    request.window =\n      request.client?.globalObject?.constructor?.name === 'Window'\n        ? request.client\n        : 'no-window'\n  }\n\n  // 9. If request’s origin is \"client\", then set request’s origin to request’s\n  // client’s origin.\n  if (request.origin === 'client') {\n    request.origin = request.client.origin\n  }\n\n  // 10. If all of the following conditions are true:\n  // TODO\n\n  // 11. If request’s policy container is \"client\", then:\n  if (request.policyContainer === 'client') {\n    // 1. If request’s client is non-null, then set request’s policy\n    // container to a clone of request’s client’s policy container. [HTML]\n    if (request.client != null) {\n      request.policyContainer = clonePolicyContainer(\n        request.client.policyContainer\n      )\n    } else {\n      // 2. Otherwise, set request’s policy container to a new policy\n      // container.\n      request.policyContainer = makePolicyContainer()\n    }\n  }\n\n  // 12. If request’s header list does not contain `Accept`, then:\n  if (!request.headersList.contains('accept', true)) {\n    // 1. Let value be `*/*`.\n    const value = '*/*'\n\n    // 2. A user agent should set value to the first matching statement, if\n    // any, switching on request’s destination:\n    // \"document\"\n    // \"frame\"\n    // \"iframe\"\n    // `text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`\n    // \"image\"\n    // `image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5`\n    // \"style\"\n    // `text/css,*/*;q=0.1`\n    // TODO\n\n    // 3. Append `Accept`/value to request’s header list.\n    request.headersList.append('accept', value, true)\n  }\n\n  // 13. If request’s header list does not contain `Accept-Language`, then\n  // user agents should append `Accept-Language`/an appropriate value to\n  // request’s header list.\n  if (!request.headersList.contains('accept-language', true)) {\n    request.headersList.append('accept-language', '*', true)\n  }\n\n  // 14. If request’s priority is null, then use request’s initiator and\n  // destination appropriately in setting request’s priority to a\n  // user-agent-defined object.\n  if (request.priority === null) {\n    // TODO\n  }\n\n  // 15. If request is a subresource request, then:\n  if (subresourceSet.has(request.destination)) {\n    // TODO\n  }\n\n  // 16. Run main fetch given fetchParams.\n  mainFetch(fetchParams, false)\n\n  // 17. Return fetchParam's controller\n  return fetchParams.controller\n}\n\n// https://fetch.spec.whatwg.org/#concept-main-fetch\nasync function mainFetch (fetchParams, recursive) {\n  try {\n    // 1. Let request be fetchParams’s request.\n    const request = fetchParams.request\n\n    // 2. Let response be null.\n    let response = null\n\n    // 3. If request’s local-URLs-only flag is set and request’s current URL is\n    // not local, then set response to a network error.\n    if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) {\n      response = makeNetworkError('local URLs only')\n    }\n\n    // 4. Run report Content Security Policy violations for request.\n    // TODO\n\n    // 5. Upgrade request to a potentially trustworthy URL, if appropriate.\n    tryUpgradeRequestToAPotentiallyTrustworthyURL(request)\n\n    // 6. If should request be blocked due to a bad port, should fetching request\n    // be blocked as mixed content, or should request be blocked by Content\n    // Security Policy returns blocked, then set response to a network error.\n    if (requestBadPort(request) === 'blocked') {\n      response = makeNetworkError('bad port')\n    }\n    // TODO: should fetching request be blocked as mixed content?\n    // TODO: should request be blocked by Content Security Policy?\n\n    // 7. If request’s referrer policy is the empty string, then set request’s\n    // referrer policy to request’s policy container’s referrer policy.\n    if (request.referrerPolicy === '') {\n      request.referrerPolicy = request.policyContainer.referrerPolicy\n    }\n\n    // 8. If request’s referrer is not \"no-referrer\", then set request’s\n    // referrer to the result of invoking determine request’s referrer.\n    if (request.referrer !== 'no-referrer') {\n      request.referrer = determineRequestsReferrer(request)\n    }\n\n    // 9. Set request’s current URL’s scheme to \"https\" if all of the following\n    // conditions are true:\n    // - request’s current URL’s scheme is \"http\"\n    // - request’s current URL’s host is a domain\n    // - Matching request’s current URL’s host per Known HSTS Host Domain Name\n    //   Matching results in either a superdomain match with an asserted\n    //   includeSubDomains directive or a congruent match (with or without an\n    //   asserted includeSubDomains directive). [HSTS]\n    // TODO\n\n    // 10. If recursive is false, then run the remaining steps in parallel.\n    // TODO\n\n    // 11. If response is null, then set response to the result of running\n    // the steps corresponding to the first matching statement:\n    if (response === null) {\n      const currentURL = requestCurrentURL(request)\n      if (\n        // - request’s current URL’s origin is same origin with request’s origin,\n        //   and request’s response tainting is \"basic\"\n        (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') ||\n        // request’s current URL’s scheme is \"data\"\n        (currentURL.protocol === 'data:') ||\n        // - request’s mode is \"navigate\" or \"websocket\"\n        (request.mode === 'navigate' || request.mode === 'websocket')\n      ) {\n        // 1. Set request’s response tainting to \"basic\".\n        request.responseTainting = 'basic'\n\n        // 2. Return the result of running scheme fetch given fetchParams.\n        response = await schemeFetch(fetchParams)\n\n      // request’s mode is \"same-origin\"\n      } else if (request.mode === 'same-origin') {\n        // 1. Return a network error.\n        response = makeNetworkError('request mode cannot be \"same-origin\"')\n\n      // request’s mode is \"no-cors\"\n      } else if (request.mode === 'no-cors') {\n        // 1. If request’s redirect mode is not \"follow\", then return a network\n        // error.\n        if (request.redirect !== 'follow') {\n          response = makeNetworkError(\n            'redirect mode cannot be \"follow\" for \"no-cors\" request'\n          )\n        } else {\n          // 2. Set request’s response tainting to \"opaque\".\n          request.responseTainting = 'opaque'\n\n          // 3. Return the result of running scheme fetch given fetchParams.\n          response = await schemeFetch(fetchParams)\n        }\n      // request’s current URL’s scheme is not an HTTP(S) scheme\n      } else if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) {\n        // Return a network error.\n        response = makeNetworkError('URL scheme must be a HTTP(S) scheme')\n\n        // - request’s use-CORS-preflight flag is set\n        // - request’s unsafe-request flag is set and either request’s method is\n        //   not a CORS-safelisted method or CORS-unsafe request-header names with\n        //   request’s header list is not empty\n        //    1. Set request’s response tainting to \"cors\".\n        //    2. Let corsWithPreflightResponse be the result of running HTTP fetch\n        //    given fetchParams and true.\n        //    3. If corsWithPreflightResponse is a network error, then clear cache\n        //    entries using request.\n        //    4. Return corsWithPreflightResponse.\n        // TODO\n\n      // Otherwise\n      } else {\n        //    1. Set request’s response tainting to \"cors\".\n        request.responseTainting = 'cors'\n\n        //    2. Return the result of running HTTP fetch given fetchParams.\n        response = await httpFetch(fetchParams)\n      }\n    }\n\n    // 12. If recursive is true, then return response.\n    if (recursive) {\n      return response\n    }\n\n    // 13. If response is not a network error and response is not a filtered\n    // response, then:\n    if (response.status !== 0 && !response.internalResponse) {\n      // If request’s response tainting is \"cors\", then:\n      if (request.responseTainting === 'cors') {\n        // 1. Let headerNames be the result of extracting header list values\n        // given `Access-Control-Expose-Headers` and response’s header list.\n        // TODO\n        // 2. If request’s credentials mode is not \"include\" and headerNames\n        // contains `*`, then set response’s CORS-exposed header-name list to\n        // all unique header names in response’s header list.\n        // TODO\n        // 3. Otherwise, if headerNames is not null or failure, then set\n        // response’s CORS-exposed header-name list to headerNames.\n        // TODO\n      }\n\n      // Set response to the following filtered response with response as its\n      // internal response, depending on request’s response tainting:\n      if (request.responseTainting === 'basic') {\n        response = filterResponse(response, 'basic')\n      } else if (request.responseTainting === 'cors') {\n        response = filterResponse(response, 'cors')\n      } else if (request.responseTainting === 'opaque') {\n        response = filterResponse(response, 'opaque')\n      } else {\n        assert(false)\n      }\n    }\n\n    // 14. Let internalResponse be response, if response is a network error,\n    // and response’s internal response otherwise.\n    let internalResponse =\n      response.status === 0 ? response : response.internalResponse\n\n    // 15. If internalResponse’s URL list is empty, then set it to a clone of\n    // request’s URL list.\n    if (internalResponse.urlList.length === 0) {\n      internalResponse.urlList.push(...request.urlList)\n    }\n\n    // 16. If request’s timing allow failed flag is unset, then set\n    // internalResponse’s timing allow passed flag.\n    if (!request.timingAllowFailed) {\n      response.timingAllowPassed = true\n    }\n\n    // 17. If response is not a network error and any of the following returns\n    // blocked\n    // - should internalResponse to request be blocked as mixed content\n    // - should internalResponse to request be blocked by Content Security Policy\n    // - should internalResponse to request be blocked due to its MIME type\n    // - should internalResponse to request be blocked due to nosniff\n    // TODO\n\n    // 18. If response’s type is \"opaque\", internalResponse’s status is 206,\n    // internalResponse’s range-requested flag is set, and request’s header\n    // list does not contain `Range`, then set response and internalResponse\n    // to a network error.\n    if (\n      response.type === 'opaque' &&\n      internalResponse.status === 206 &&\n      internalResponse.rangeRequested &&\n      !request.headers.contains('range', true)\n    ) {\n      response = internalResponse = makeNetworkError()\n    }\n\n    // 19. If response is not a network error and either request’s method is\n    // `HEAD` or `CONNECT`, or internalResponse’s status is a null body status,\n    // set internalResponse’s body to null and disregard any enqueuing toward\n    // it (if any).\n    if (\n      response.status !== 0 &&\n      (request.method === 'HEAD' ||\n        request.method === 'CONNECT' ||\n        nullBodyStatus.includes(internalResponse.status))\n    ) {\n      internalResponse.body = null\n      fetchParams.controller.dump = true\n    }\n\n    // 20. If request’s integrity metadata is not the empty string, then:\n    if (request.integrity) {\n      // 1. Let processBodyError be this step: run fetch finale given fetchParams\n      // and a network error.\n      const processBodyError = (reason) =>\n        fetchFinale(fetchParams, makeNetworkError(reason))\n\n      // 2. If request’s response tainting is \"opaque\", or response’s body is null,\n      // then run processBodyError and abort these steps.\n      if (request.responseTainting === 'opaque' || response.body == null) {\n        processBodyError(response.error)\n        return\n      }\n\n      // 3. Let processBody given bytes be these steps:\n      const processBody = (bytes) => {\n        // 1. If bytes do not match request’s integrity metadata,\n        // then run processBodyError and abort these steps. [SRI]\n        if (!bytesMatch(bytes, request.integrity)) {\n          processBodyError('integrity mismatch')\n          return\n        }\n\n        // 2. Set response’s body to bytes as a body.\n        response.body = safelyExtractBody(bytes)[0]\n\n        // 3. Run fetch finale given fetchParams and response.\n        fetchFinale(fetchParams, response)\n      }\n\n      // 4. Fully read response’s body given processBody and processBodyError.\n      fullyReadBody(response.body, processBody, processBodyError)\n    } else {\n      // 21. Otherwise, run fetch finale given fetchParams and response.\n      fetchFinale(fetchParams, response)\n    }\n  } catch (err) {\n    fetchParams.controller.terminate(err)\n  }\n}\n\n// https://fetch.spec.whatwg.org/#concept-scheme-fetch\n// given a fetch params fetchParams\nfunction schemeFetch (fetchParams) {\n  // Note: since the connection is destroyed on redirect, which sets fetchParams to a\n  // cancelled state, we do not want this condition to trigger *unless* there have been\n  // no redirects. See https://github.com/nodejs/undici/issues/1776\n  // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.\n  if (isCancelled(fetchParams) && fetchParams.request.redirectCount === 0) {\n    return Promise.resolve(makeAppropriateNetworkError(fetchParams))\n  }\n\n  // 2. Let request be fetchParams’s request.\n  const { request } = fetchParams\n\n  const { protocol: scheme } = requestCurrentURL(request)\n\n  // 3. Switch on request’s current URL’s scheme and run the associated steps:\n  switch (scheme) {\n    case 'about:': {\n      // If request’s current URL’s path is the string \"blank\", then return a new response\n      // whose status message is `OK`, header list is « (`Content-Type`, `text/html;charset=utf-8`) »,\n      // and body is the empty byte sequence as a body.\n\n      // Otherwise, return a network error.\n      return Promise.resolve(makeNetworkError('about scheme is not supported'))\n    }\n    case 'blob:': {\n      if (!resolveObjectURL) {\n        resolveObjectURL = require('node:buffer').resolveObjectURL\n      }\n\n      // 1. Let blobURLEntry be request’s current URL’s blob URL entry.\n      const blobURLEntry = requestCurrentURL(request)\n\n      // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56\n      // Buffer.resolveObjectURL does not ignore URL queries.\n      if (blobURLEntry.search.length !== 0) {\n        return Promise.resolve(makeNetworkError('NetworkError when attempting to fetch resource.'))\n      }\n\n      const blob = resolveObjectURL(blobURLEntry.toString())\n\n      // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s\n      //    object is not a Blob object, then return a network error.\n      if (request.method !== 'GET' || !webidl.is.Blob(blob)) {\n        return Promise.resolve(makeNetworkError('invalid method'))\n      }\n\n      // 3. Let blob be blobURLEntry’s object.\n      // Note: done above\n\n      // 4. Let response be a new response.\n      const response = makeResponse()\n\n      // 5. Let fullLength be blob’s size.\n      const fullLength = blob.size\n\n      // 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded.\n      const serializedFullLength = isomorphicEncode(`${fullLength}`)\n\n      // 7. Let type be blob’s type.\n      const type = blob.type\n\n      // 8. If request’s header list does not contain `Range`:\n      // 9. Otherwise:\n      if (!request.headersList.contains('range', true)) {\n        // 1. Let bodyWithType be the result of safely extracting blob.\n        // Note: in the FileAPI a blob \"object\" is a Blob *or* a MediaSource.\n        // In node, this can only ever be a Blob. Therefore we can safely\n        // use extractBody directly.\n        const bodyWithType = extractBody(blob)\n\n        // 2. Set response’s status message to `OK`.\n        response.statusText = 'OK'\n\n        // 3. Set response’s body to bodyWithType’s body.\n        response.body = bodyWithType[0]\n\n        // 4. Set response’s header list to « (`Content-Length`, serializedFullLength), (`Content-Type`, type) ».\n        response.headersList.set('content-length', serializedFullLength, true)\n        response.headersList.set('content-type', type, true)\n      } else {\n        // 1. Set response’s range-requested flag.\n        response.rangeRequested = true\n\n        // 2. Let rangeHeader be the result of getting `Range` from request’s header list.\n        const rangeHeader = request.headersList.get('range', true)\n\n        // 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true.\n        const rangeValue = simpleRangeHeaderValue(rangeHeader, true)\n\n        // 4. If rangeValue is failure, then return a network error.\n        if (rangeValue === 'failure') {\n          return Promise.resolve(makeNetworkError('failed to fetch the data URL'))\n        }\n\n        // 5. Let (rangeStart, rangeEnd) be rangeValue.\n        let { rangeStartValue: rangeStart, rangeEndValue: rangeEnd } = rangeValue\n\n        // 6. If rangeStart is null:\n        // 7. Otherwise:\n        if (rangeStart === null) {\n          // 1. Set rangeStart to fullLength − rangeEnd.\n          rangeStart = fullLength - rangeEnd\n\n          // 2. Set rangeEnd to rangeStart + rangeEnd − 1.\n          rangeEnd = rangeStart + rangeEnd - 1\n        } else {\n          // 1. If rangeStart is greater than or equal to fullLength, then return a network error.\n          if (rangeStart >= fullLength) {\n            return Promise.resolve(makeNetworkError('Range start is greater than the blob\\'s size.'))\n          }\n\n          // 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set\n          //    rangeEnd to fullLength − 1.\n          if (rangeEnd === null || rangeEnd >= fullLength) {\n            rangeEnd = fullLength - 1\n          }\n        }\n\n        // 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart,\n        //    rangeEnd + 1, and type.\n        const slicedBlob = blob.slice(rangeStart, rangeEnd + 1, type)\n\n        // 9. Let slicedBodyWithType be the result of safely extracting slicedBlob.\n        // Note: same reason as mentioned above as to why we use extractBody\n        const slicedBodyWithType = extractBody(slicedBlob)\n\n        // 10. Set response’s body to slicedBodyWithType’s body.\n        response.body = slicedBodyWithType[0]\n\n        // 11. Let serializedSlicedLength be slicedBlob’s size, serialized and isomorphic encoded.\n        const serializedSlicedLength = isomorphicEncode(`${slicedBlob.size}`)\n\n        // 12. Let contentRange be the result of invoking build a content range given rangeStart,\n        //     rangeEnd, and fullLength.\n        const contentRange = buildContentRange(rangeStart, rangeEnd, fullLength)\n\n        // 13. Set response’s status to 206.\n        response.status = 206\n\n        // 14. Set response’s status message to `Partial Content`.\n        response.statusText = 'Partial Content'\n\n        // 15. Set response’s header list to « (`Content-Length`, serializedSlicedLength),\n        //     (`Content-Type`, type), (`Content-Range`, contentRange) ».\n        response.headersList.set('content-length', serializedSlicedLength, true)\n        response.headersList.set('content-type', type, true)\n        response.headersList.set('content-range', contentRange, true)\n      }\n\n      // 10. Return response.\n      return Promise.resolve(response)\n    }\n    case 'data:': {\n      // 1. Let dataURLStruct be the result of running the\n      //    data: URL processor on request’s current URL.\n      const currentURL = requestCurrentURL(request)\n      const dataURLStruct = dataURLProcessor(currentURL)\n\n      // 2. If dataURLStruct is failure, then return a\n      //    network error.\n      if (dataURLStruct === 'failure') {\n        return Promise.resolve(makeNetworkError('failed to fetch the data URL'))\n      }\n\n      // 3. Let mimeType be dataURLStruct’s MIME type, serialized.\n      const mimeType = serializeAMimeType(dataURLStruct.mimeType)\n\n      // 4. Return a response whose status message is `OK`,\n      //    header list is « (`Content-Type`, mimeType) »,\n      //    and body is dataURLStruct’s body as a body.\n      return Promise.resolve(makeResponse({\n        statusText: 'OK',\n        headersList: [\n          ['content-type', { name: 'Content-Type', value: mimeType }]\n        ],\n        body: safelyExtractBody(dataURLStruct.body)[0]\n      }))\n    }\n    case 'file:': {\n      // For now, unfortunate as it is, file URLs are left as an exercise for the reader.\n      // When in doubt, return a network error.\n      return Promise.resolve(makeNetworkError('not implemented... yet...'))\n    }\n    case 'http:':\n    case 'https:': {\n      // Return the result of running HTTP fetch given fetchParams.\n\n      return httpFetch(fetchParams)\n        .catch((err) => makeNetworkError(err))\n    }\n    default: {\n      return Promise.resolve(makeNetworkError('unknown scheme'))\n    }\n  }\n}\n\n// https://fetch.spec.whatwg.org/#finalize-response\nfunction finalizeResponse (fetchParams, response) {\n  // 1. Set fetchParams’s request’s done flag.\n  fetchParams.request.done = true\n\n  // 2, If fetchParams’s process response done is not null, then queue a fetch\n  // task to run fetchParams’s process response done given response, with\n  // fetchParams’s task destination.\n  if (fetchParams.processResponseDone != null) {\n    queueMicrotask(() => fetchParams.processResponseDone(response))\n  }\n}\n\n// https://fetch.spec.whatwg.org/#fetch-finale\nfunction fetchFinale (fetchParams, response) {\n  // 1. Let timingInfo be fetchParams’s timing info.\n  let timingInfo = fetchParams.timingInfo\n\n  // 2. If response is not a network error and fetchParams’s request’s client is a secure context,\n  //    then set timingInfo’s server-timing headers to the result of getting, decoding, and splitting\n  //    `Server-Timing` from response’s internal response’s header list.\n  // TODO\n\n  // 3. Let processResponseEndOfBody be the following steps:\n  const processResponseEndOfBody = () => {\n    // 1. Let unsafeEndTime be the unsafe shared current time.\n    const unsafeEndTime = Date.now() // ?\n\n    // 2. If fetchParams’s request’s destination is \"document\", then set fetchParams’s controller’s\n    //    full timing info to fetchParams’s timing info.\n    if (fetchParams.request.destination === 'document') {\n      fetchParams.controller.fullTimingInfo = timingInfo\n    }\n\n    // 3. Set fetchParams’s controller’s report timing steps to the following steps given a global object global:\n    fetchParams.controller.reportTimingSteps = () => {\n      // 1. If fetchParams’s request’s URL’s scheme is not an HTTP(S) scheme, then return.\n      if (!urlIsHttpHttpsScheme(fetchParams.request.url)) {\n        return\n      }\n\n      // 2. Set timingInfo’s end time to the relative high resolution time given unsafeEndTime and global.\n      timingInfo.endTime = unsafeEndTime\n\n      // 3. Let cacheState be response’s cache state.\n      let cacheState = response.cacheState\n\n      // 4. Let bodyInfo be response’s body info.\n      const bodyInfo = response.bodyInfo\n\n      // 5. If response’s timing allow passed flag is not set, then set timingInfo to the result of creating an\n      //    opaque timing info for timingInfo and set cacheState to the empty string.\n      if (!response.timingAllowPassed) {\n        timingInfo = createOpaqueTimingInfo(timingInfo)\n\n        cacheState = ''\n      }\n\n      // 6. Let responseStatus be 0.\n      let responseStatus = 0\n\n      // 7. If fetchParams’s request’s mode is not \"navigate\" or response’s has-cross-origin-redirects is false:\n      if (fetchParams.request.mode !== 'navigator' || !response.hasCrossOriginRedirects) {\n        // 1. Set responseStatus to response’s status.\n        responseStatus = response.status\n\n        // 2. Let mimeType be the result of extracting a MIME type from response’s header list.\n        const mimeType = extractMimeType(response.headersList)\n\n        // 3. If mimeType is not failure, then set bodyInfo’s content type to the result of minimizing a supported MIME type given mimeType.\n        if (mimeType !== 'failure') {\n          bodyInfo.contentType = minimizeSupportedMimeType(mimeType)\n        }\n      }\n\n      // 8. If fetchParams’s request’s initiator type is non-null, then mark resource timing given timingInfo,\n      //    fetchParams’s request’s URL, fetchParams’s request’s initiator type, global, cacheState, bodyInfo,\n      //    and responseStatus.\n      if (fetchParams.request.initiatorType != null) {\n        markResourceTiming(timingInfo, fetchParams.request.url.href, fetchParams.request.initiatorType, globalThis, cacheState, bodyInfo, responseStatus)\n      }\n    }\n\n    // 4. Let processResponseEndOfBodyTask be the following steps:\n    const processResponseEndOfBodyTask = () => {\n      // 1. Set fetchParams’s request’s done flag.\n      fetchParams.request.done = true\n\n      // 2. If fetchParams’s process response end-of-body is non-null, then run fetchParams’s process\n      //    response end-of-body given response.\n      if (fetchParams.processResponseEndOfBody != null) {\n        queueMicrotask(() => fetchParams.processResponseEndOfBody(response))\n      }\n\n      // 3. If fetchParams’s request’s initiator type is non-null and fetchParams’s request’s client’s\n      //    global object is fetchParams’s task destination, then run fetchParams’s controller’s report\n      //    timing steps given fetchParams’s request’s client’s global object.\n      if (fetchParams.request.initiatorType != null) {\n        fetchParams.controller.reportTimingSteps()\n      }\n    }\n\n    // 5. Queue a fetch task to run processResponseEndOfBodyTask with fetchParams’s task destination\n    queueMicrotask(() => processResponseEndOfBodyTask())\n  }\n\n  // 4. If fetchParams’s process response is non-null, then queue a fetch task to run fetchParams’s\n  //    process response given response, with fetchParams’s task destination.\n  if (fetchParams.processResponse != null) {\n    queueMicrotask(() => {\n      fetchParams.processResponse(response)\n      fetchParams.processResponse = null\n    })\n  }\n\n  // 5. Let internalResponse be response, if response is a network error; otherwise response’s internal response.\n  const internalResponse = response.type === 'error' ? response : (response.internalResponse ?? response)\n\n  // 6. If internalResponse’s body is null, then run processResponseEndOfBody.\n  // 7. Otherwise:\n  if (internalResponse.body == null) {\n    processResponseEndOfBody()\n  } else {\n    // mcollina: all the following steps of the specs are skipped.\n    // The internal transform stream is not needed.\n    // See https://github.com/nodejs/undici/pull/3093#issuecomment-2050198541\n\n    // 1. Let transformStream be a new TransformStream.\n    // 2. Let identityTransformAlgorithm be an algorithm which, given chunk, enqueues chunk in transformStream.\n    // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm and flushAlgorithm\n    //    set to processResponseEndOfBody.\n    // 4. Set internalResponse’s body’s stream to the result of internalResponse’s body’s stream piped through transformStream.\n\n    finished(internalResponse.body.stream, () => {\n      processResponseEndOfBody()\n    })\n  }\n}\n\n// https://fetch.spec.whatwg.org/#http-fetch\nasync function httpFetch (fetchParams) {\n  // 1. Let request be fetchParams’s request.\n  const request = fetchParams.request\n\n  // 2. Let response be null.\n  let response = null\n\n  // 3. Let actualResponse be null.\n  let actualResponse = null\n\n  // 4. Let timingInfo be fetchParams’s timing info.\n  const timingInfo = fetchParams.timingInfo\n\n  // 5. If request’s service-workers mode is \"all\", then:\n  if (request.serviceWorkers === 'all') {\n    // TODO\n  }\n\n  // 6. If response is null, then:\n  if (response === null) {\n    // 1. If makeCORSPreflight is true and one of these conditions is true:\n    // TODO\n\n    // 2. If request’s redirect mode is \"follow\", then set request’s\n    // service-workers mode to \"none\".\n    if (request.redirect === 'follow') {\n      request.serviceWorkers = 'none'\n    }\n\n    // 3. Set response and actualResponse to the result of running\n    // HTTP-network-or-cache fetch given fetchParams.\n    actualResponse = response = await httpNetworkOrCacheFetch(fetchParams)\n\n    // 4. If request’s response tainting is \"cors\" and a CORS check\n    // for request and response returns failure, then return a network error.\n    if (\n      request.responseTainting === 'cors' &&\n      corsCheck(request, response) === 'failure'\n    ) {\n      return makeNetworkError('cors failure')\n    }\n\n    // 5. If the TAO check for request and response returns failure, then set\n    // request’s timing allow failed flag.\n    if (TAOCheck(request, response) === 'failure') {\n      request.timingAllowFailed = true\n    }\n  }\n\n  // 7. If either request’s response tainting or response’s type\n  // is \"opaque\", and the cross-origin resource policy check with\n  // request’s origin, request’s client, request’s destination,\n  // and actualResponse returns blocked, then return a network error.\n  if (\n    (request.responseTainting === 'opaque' || response.type === 'opaque') &&\n    crossOriginResourcePolicyCheck(\n      request.origin,\n      request.client,\n      request.destination,\n      actualResponse\n    ) === 'blocked'\n  ) {\n    return makeNetworkError('blocked')\n  }\n\n  // 8. If actualResponse’s status is a redirect status, then:\n  if (redirectStatusSet.has(actualResponse.status)) {\n    // 1. If actualResponse’s status is not 303, request’s body is not null,\n    // and the connection uses HTTP/2, then user agents may, and are even\n    // encouraged to, transmit an RST_STREAM frame.\n    // See, https://github.com/whatwg/fetch/issues/1288\n    if (request.redirect !== 'manual') {\n      fetchParams.controller.connection.destroy(undefined, false)\n    }\n\n    // 2. Switch on request’s redirect mode:\n    if (request.redirect === 'error') {\n      // Set response to a network error.\n      response = makeNetworkError('unexpected redirect')\n    } else if (request.redirect === 'manual') {\n      // Set response to an opaque-redirect filtered response whose internal\n      // response is actualResponse.\n      // NOTE(spec): On the web this would return an `opaqueredirect` response,\n      // but that doesn't make sense server side.\n      // See https://github.com/nodejs/undici/issues/1193.\n      response = actualResponse\n    } else if (request.redirect === 'follow') {\n      // Set response to the result of running HTTP-redirect fetch given\n      // fetchParams and response.\n      response = await httpRedirectFetch(fetchParams, response)\n    } else {\n      assert(false)\n    }\n  }\n\n  // 9. Set response’s timing info to timingInfo.\n  response.timingInfo = timingInfo\n\n  // 10. Return response.\n  return response\n}\n\n// https://fetch.spec.whatwg.org/#http-redirect-fetch\nfunction httpRedirectFetch (fetchParams, response) {\n  // 1. Let request be fetchParams’s request.\n  const request = fetchParams.request\n\n  // 2. Let actualResponse be response, if response is not a filtered response,\n  // and response’s internal response otherwise.\n  const actualResponse = response.internalResponse\n    ? response.internalResponse\n    : response\n\n  // 3. Let locationURL be actualResponse’s location URL given request’s current\n  // URL’s fragment.\n  let locationURL\n\n  try {\n    locationURL = responseLocationURL(\n      actualResponse,\n      requestCurrentURL(request).hash\n    )\n\n    // 4. If locationURL is null, then return response.\n    if (locationURL == null) {\n      return response\n    }\n  } catch (err) {\n    // 5. If locationURL is failure, then return a network error.\n    return Promise.resolve(makeNetworkError(err))\n  }\n\n  // 6. If locationURL’s scheme is not an HTTP(S) scheme, then return a network\n  // error.\n  if (!urlIsHttpHttpsScheme(locationURL)) {\n    return Promise.resolve(makeNetworkError('URL scheme must be a HTTP(S) scheme'))\n  }\n\n  // 7. If request’s redirect count is 20, then return a network error.\n  if (request.redirectCount === 20) {\n    return Promise.resolve(makeNetworkError('redirect count exceeded'))\n  }\n\n  // 8. Increase request’s redirect count by 1.\n  request.redirectCount += 1\n\n  // 9. If request’s mode is \"cors\", locationURL includes credentials, and\n  // request’s origin is not same origin with locationURL’s origin, then return\n  //  a network error.\n  if (\n    request.mode === 'cors' &&\n    (locationURL.username || locationURL.password) &&\n    !sameOrigin(request, locationURL)\n  ) {\n    return Promise.resolve(makeNetworkError('cross origin not allowed for request mode \"cors\"'))\n  }\n\n  // 10. If request’s response tainting is \"cors\" and locationURL includes\n  // credentials, then return a network error.\n  if (\n    request.responseTainting === 'cors' &&\n    (locationURL.username || locationURL.password)\n  ) {\n    return Promise.resolve(makeNetworkError(\n      'URL cannot contain credentials for request mode \"cors\"'\n    ))\n  }\n\n  // 11. If actualResponse’s status is not 303, request’s body is non-null,\n  // and request’s body’s source is null, then return a network error.\n  if (\n    actualResponse.status !== 303 &&\n    request.body != null &&\n    request.body.source == null\n  ) {\n    return Promise.resolve(makeNetworkError())\n  }\n\n  // 12. If one of the following is true\n  // - actualResponse’s status is 301 or 302 and request’s method is `POST`\n  // - actualResponse’s status is 303 and request’s method is not `GET` or `HEAD`\n  if (\n    ([301, 302].includes(actualResponse.status) && request.method === 'POST') ||\n    (actualResponse.status === 303 &&\n      !GET_OR_HEAD.includes(request.method))\n  ) {\n    // then:\n    // 1. Set request’s method to `GET` and request’s body to null.\n    request.method = 'GET'\n    request.body = null\n\n    // 2. For each headerName of request-body-header name, delete headerName from\n    // request’s header list.\n    for (const headerName of requestBodyHeader) {\n      request.headersList.delete(headerName)\n    }\n  }\n\n  // 13. If request’s current URL’s origin is not same origin with locationURL’s\n  //     origin, then for each headerName of CORS non-wildcard request-header name,\n  //     delete headerName from request’s header list.\n  if (!sameOrigin(requestCurrentURL(request), locationURL)) {\n    // https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name\n    request.headersList.delete('authorization', true)\n\n    // https://fetch.spec.whatwg.org/#authentication-entries\n    request.headersList.delete('proxy-authorization', true)\n\n    // \"Cookie\" and \"Host\" are forbidden request-headers, which undici doesn't implement.\n    request.headersList.delete('cookie', true)\n    request.headersList.delete('host', true)\n  }\n\n  // 14. If request's body is non-null, then set request's body to the first return\n  // value of safely extracting request's body's source.\n  if (request.body != null) {\n    assert(request.body.source != null)\n    request.body = safelyExtractBody(request.body.source)[0]\n  }\n\n  // 15. Let timingInfo be fetchParams’s timing info.\n  const timingInfo = fetchParams.timingInfo\n\n  // 16. Set timingInfo’s redirect end time and post-redirect start time to the\n  // coarsened shared current time given fetchParams’s cross-origin isolated\n  // capability.\n  timingInfo.redirectEndTime = timingInfo.postRedirectStartTime =\n    coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)\n\n  // 17. If timingInfo’s redirect start time is 0, then set timingInfo’s\n  //  redirect start time to timingInfo’s start time.\n  if (timingInfo.redirectStartTime === 0) {\n    timingInfo.redirectStartTime = timingInfo.startTime\n  }\n\n  // 18. Append locationURL to request’s URL list.\n  request.urlList.push(locationURL)\n\n  // 19. Invoke set request’s referrer policy on redirect on request and\n  // actualResponse.\n  setRequestReferrerPolicyOnRedirect(request, actualResponse)\n\n  // 20. Return the result of running main fetch given fetchParams and true.\n  return mainFetch(fetchParams, true)\n}\n\n// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch\nasync function httpNetworkOrCacheFetch (\n  fetchParams,\n  isAuthenticationFetch = false,\n  isNewConnectionFetch = false\n) {\n  // 1. Let request be fetchParams’s request.\n  const request = fetchParams.request\n\n  // 2. Let httpFetchParams be null.\n  let httpFetchParams = null\n\n  // 3. Let httpRequest be null.\n  let httpRequest = null\n\n  // 4. Let response be null.\n  let response = null\n\n  // 5. Let storedResponse be null.\n  // TODO: cache\n\n  // 6. Let httpCache be null.\n  const httpCache = null\n\n  // 7. Let the revalidatingFlag be unset.\n  const revalidatingFlag = false\n\n  // 8. Run these steps, but abort when the ongoing fetch is terminated:\n\n  //    1. If request’s window is \"no-window\" and request’s redirect mode is\n  //    \"error\", then set httpFetchParams to fetchParams and httpRequest to\n  //    request.\n  if (request.window === 'no-window' && request.redirect === 'error') {\n    httpFetchParams = fetchParams\n    httpRequest = request\n  } else {\n    // Otherwise:\n\n    // 1. Set httpRequest to a clone of request.\n    httpRequest = cloneRequest(request)\n\n    // 2. Set httpFetchParams to a copy of fetchParams.\n    httpFetchParams = { ...fetchParams }\n\n    // 3. Set httpFetchParams’s request to httpRequest.\n    httpFetchParams.request = httpRequest\n  }\n\n  //    3. Let includeCredentials be true if one of\n  const includeCredentials =\n    request.credentials === 'include' ||\n    (request.credentials === 'same-origin' &&\n      request.responseTainting === 'basic')\n\n  //    4. Let contentLength be httpRequest’s body’s length, if httpRequest’s\n  //    body is non-null; otherwise null.\n  const contentLength = httpRequest.body ? httpRequest.body.length : null\n\n  //    5. Let contentLengthHeaderValue be null.\n  let contentLengthHeaderValue = null\n\n  //    6. If httpRequest’s body is null and httpRequest’s method is `POST` or\n  //    `PUT`, then set contentLengthHeaderValue to `0`.\n  if (\n    httpRequest.body == null &&\n    ['POST', 'PUT'].includes(httpRequest.method)\n  ) {\n    contentLengthHeaderValue = '0'\n  }\n\n  //    7. If contentLength is non-null, then set contentLengthHeaderValue to\n  //    contentLength, serialized and isomorphic encoded.\n  if (contentLength != null) {\n    contentLengthHeaderValue = isomorphicEncode(`${contentLength}`)\n  }\n\n  //    8. If contentLengthHeaderValue is non-null, then append\n  //    `Content-Length`/contentLengthHeaderValue to httpRequest’s header\n  //    list.\n  if (contentLengthHeaderValue != null) {\n    httpRequest.headersList.append('content-length', contentLengthHeaderValue, true)\n  }\n\n  //    9. If contentLengthHeaderValue is non-null, then append (`Content-Length`,\n  //    contentLengthHeaderValue) to httpRequest’s header list.\n\n  //    10. If contentLength is non-null and httpRequest’s keepalive is true,\n  //    then:\n  if (contentLength != null && httpRequest.keepalive) {\n    // NOTE: keepalive is a noop outside of browser context.\n  }\n\n  //    11. If httpRequest’s referrer is a URL, then append\n  //    `Referer`/httpRequest’s referrer, serialized and isomorphic encoded,\n  //     to httpRequest’s header list.\n  if (webidl.is.URL(httpRequest.referrer)) {\n    httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href), true)\n  }\n\n  //    12. Append a request `Origin` header for httpRequest.\n  appendRequestOriginHeader(httpRequest)\n\n  //    13. Append the Fetch metadata headers for httpRequest. [FETCH-METADATA]\n  appendFetchMetadata(httpRequest)\n\n  //    14. If httpRequest’s header list does not contain `User-Agent`, then\n  //    user agents should append `User-Agent`/default `User-Agent` value to\n  //    httpRequest’s header list.\n  if (!httpRequest.headersList.contains('user-agent', true)) {\n    httpRequest.headersList.append('user-agent', defaultUserAgent, true)\n  }\n\n  //    15. If httpRequest’s cache mode is \"default\" and httpRequest’s header\n  //    list contains `If-Modified-Since`, `If-None-Match`,\n  //    `If-Unmodified-Since`, `If-Match`, or `If-Range`, then set\n  //    httpRequest’s cache mode to \"no-store\".\n  if (\n    httpRequest.cache === 'default' &&\n    (httpRequest.headersList.contains('if-modified-since', true) ||\n      httpRequest.headersList.contains('if-none-match', true) ||\n      httpRequest.headersList.contains('if-unmodified-since', true) ||\n      httpRequest.headersList.contains('if-match', true) ||\n      httpRequest.headersList.contains('if-range', true))\n  ) {\n    httpRequest.cache = 'no-store'\n  }\n\n  //    16. If httpRequest’s cache mode is \"no-cache\", httpRequest’s prevent\n  //    no-cache cache-control header modification flag is unset, and\n  //    httpRequest’s header list does not contain `Cache-Control`, then append\n  //    `Cache-Control`/`max-age=0` to httpRequest’s header list.\n  if (\n    httpRequest.cache === 'no-cache' &&\n    !httpRequest.preventNoCacheCacheControlHeaderModification &&\n    !httpRequest.headersList.contains('cache-control', true)\n  ) {\n    httpRequest.headersList.append('cache-control', 'max-age=0', true)\n  }\n\n  //    17. If httpRequest’s cache mode is \"no-store\" or \"reload\", then:\n  if (httpRequest.cache === 'no-store' || httpRequest.cache === 'reload') {\n    // 1. If httpRequest’s header list does not contain `Pragma`, then append\n    // `Pragma`/`no-cache` to httpRequest’s header list.\n    if (!httpRequest.headersList.contains('pragma', true)) {\n      httpRequest.headersList.append('pragma', 'no-cache', true)\n    }\n\n    // 2. If httpRequest’s header list does not contain `Cache-Control`,\n    // then append `Cache-Control`/`no-cache` to httpRequest’s header list.\n    if (!httpRequest.headersList.contains('cache-control', true)) {\n      httpRequest.headersList.append('cache-control', 'no-cache', true)\n    }\n  }\n\n  //    18. If httpRequest’s header list contains `Range`, then append\n  //    `Accept-Encoding`/`identity` to httpRequest’s header list.\n  if (httpRequest.headersList.contains('range', true)) {\n    httpRequest.headersList.append('accept-encoding', 'identity', true)\n  }\n\n  //    19. Modify httpRequest’s header list per HTTP. Do not append a given\n  //    header if httpRequest’s header list contains that header’s name.\n  //    TODO: https://github.com/whatwg/fetch/issues/1285#issuecomment-896560129\n  if (!httpRequest.headersList.contains('accept-encoding', true)) {\n    if (urlHasHttpsScheme(requestCurrentURL(httpRequest))) {\n      httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate', true)\n    } else {\n      httpRequest.headersList.append('accept-encoding', 'gzip, deflate', true)\n    }\n  }\n\n  httpRequest.headersList.delete('host', true)\n\n  //    21. If includeCredentials is true, then:\n  if (includeCredentials) {\n    // 1. If the user agent is not configured to block cookies for httpRequest\n    // (see section 7 of [COOKIES]), then:\n    // TODO: credentials\n\n    // 2. If httpRequest’s header list does not contain `Authorization`, then:\n    if (!httpRequest.headersList.contains('authorization', true)) {\n      // 1. Let authorizationValue be null.\n      let authorizationValue = null\n\n      // 2. If there’s an authentication entry for httpRequest and either\n      //    httpRequest’s use-URL-credentials flag is unset or httpRequest’s\n      //    current URL does not include credentials, then set\n      //    authorizationValue to authentication entry.\n      if (hasAuthenticationEntry(httpRequest) && (\n        httpRequest.useURLCredentials === undefined || !includesCredentials(requestCurrentURL(httpRequest))\n      )) {\n        // TODO\n      } else if (includesCredentials(requestCurrentURL(httpRequest)) && isAuthenticationFetch) {\n        // 3. Otherwise, if httpRequest’s current URL does include credentials\n        //    and isAuthenticationFetch is true, set authorizationValue to\n        //    httpRequest’s current URL, converted to an `Authorization` value\n        const { username, password } = requestCurrentURL(httpRequest)\n        authorizationValue = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`\n      }\n\n      // 4. If authorizationValue is non-null, then append (`Authorization`,\n      //    authorizationValue) to httpRequest’s header list.\n      if (authorizationValue !== null) {\n        httpRequest.headersList.append('Authorization', authorizationValue, false)\n      }\n    }\n  }\n\n  //    21. If there’s a proxy-authentication entry, use it as appropriate.\n  //    TODO: proxy-authentication\n\n  //    22. Set httpCache to the result of determining the HTTP cache\n  //    partition, given httpRequest.\n  //    TODO: cache\n\n  //    23. If httpCache is null, then set httpRequest’s cache mode to\n  //    \"no-store\".\n  if (httpCache == null) {\n    httpRequest.cache = 'no-store'\n  }\n\n  //    24. If httpRequest’s cache mode is neither \"no-store\" nor \"reload\",\n  //    then:\n  if (httpRequest.cache !== 'no-store' && httpRequest.cache !== 'reload') {\n    // TODO: cache\n  }\n\n  // 9. If aborted, then return the appropriate network error for fetchParams.\n  // TODO\n\n  // 10. If response is null, then:\n  if (response == null) {\n    // 1. If httpRequest’s cache mode is \"only-if-cached\", then return a\n    // network error.\n    if (httpRequest.cache === 'only-if-cached') {\n      return makeNetworkError('only if cached')\n    }\n\n    // 2. Let forwardResponse be the result of running HTTP-network fetch\n    // given httpFetchParams, includeCredentials, and isNewConnectionFetch.\n    const forwardResponse = await httpNetworkFetch(\n      httpFetchParams,\n      includeCredentials,\n      isNewConnectionFetch\n    )\n\n    // 3. If httpRequest’s method is unsafe and forwardResponse’s status is\n    // in the range 200 to 399, inclusive, invalidate appropriate stored\n    // responses in httpCache, as per the \"Invalidation\" chapter of HTTP\n    // Caching, and set storedResponse to null. [HTTP-CACHING]\n    if (\n      !safeMethodsSet.has(httpRequest.method) &&\n      forwardResponse.status >= 200 &&\n      forwardResponse.status <= 399\n    ) {\n      // TODO: cache\n    }\n\n    // 4. If the revalidatingFlag is set and forwardResponse’s status is 304,\n    // then:\n    if (revalidatingFlag && forwardResponse.status === 304) {\n      // TODO: cache\n    }\n\n    // 5. If response is null, then:\n    if (response == null) {\n      // 1. Set response to forwardResponse.\n      response = forwardResponse\n\n      // 2. Store httpRequest and forwardResponse in httpCache, as per the\n      // \"Storing Responses in Caches\" chapter of HTTP Caching. [HTTP-CACHING]\n      // TODO: cache\n    }\n  }\n\n  // 11. Set response’s URL list to a clone of httpRequest’s URL list.\n  response.urlList = [...httpRequest.urlList]\n\n  // 12. If httpRequest’s header list contains `Range`, then set response’s\n  // range-requested flag.\n  if (httpRequest.headersList.contains('range', true)) {\n    response.rangeRequested = true\n  }\n\n  // 13. Set response’s request-includes-credentials to includeCredentials.\n  response.requestIncludesCredentials = includeCredentials\n\n  // 14. If response’s status is 401, httpRequest’s response tainting is not \"cors\",\n  //     includeCredentials is true, and request’s traversable for user prompts is\n  //     a traversable navigable:\n  if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) {\n    // 2. If request’s body is non-null, then:\n    if (request.body != null) {\n      // 1. If request’s body’s source is null, then return a network error.\n      if (request.body.source == null) {\n        return makeNetworkError('expected non-null body source')\n      }\n\n      // 2. Set request’s body to the body of the result of safely extracting\n      //    request’s body’s source.\n      request.body = safelyExtractBody(request.body.source)[0]\n    }\n\n    // 3. If request’s use-URL-credentials flag is unset or isAuthenticationFetch is\n    //    true, then:\n    if (request.useURLCredentials === undefined || isAuthenticationFetch) {\n      // 1. If fetchParams is canceled, then return the appropriate network error\n      //    for fetchParams.\n      if (isCancelled(fetchParams)) {\n        return makeAppropriateNetworkError(fetchParams)\n      }\n\n      // 2. Let username and password be the result of prompting the end user for a\n      //    username and password, respectively, in request’s traversable for user prompts.\n      // TODO\n\n      // 3. Set the username given request’s current URL and username.\n      // requestCurrentURL(request).username = TODO\n\n      // 4. Set the password given request’s current URL and password.\n      // requestCurrentURL(request).password = TODO\n\n      // In browsers, the user will be prompted to enter a username/password before the request\n      // is re-sent. To prevent an infinite 401 loop, return the response for now.\n      // https://github.com/nodejs/undici/pull/4756\n      return response\n    }\n\n    // 4. Set response to the result of running HTTP-network-or-cache fetch given\n    //    fetchParams and true.\n    fetchParams.controller.connection.destroy()\n\n    response = await httpNetworkOrCacheFetch(fetchParams, true)\n  }\n\n  // 15. If response’s status is 407, then:\n  if (response.status === 407) {\n    // 1. If request’s window is \"no-window\", then return a network error.\n    if (request.window === 'no-window') {\n      return makeNetworkError()\n    }\n\n    // 2. ???\n\n    // 3. If fetchParams is canceled, then return the appropriate network error for fetchParams.\n    if (isCancelled(fetchParams)) {\n      return makeAppropriateNetworkError(fetchParams)\n    }\n\n    // 4. Prompt the end user as appropriate in request’s window and store\n    // the result as a proxy-authentication entry. [HTTP-AUTH]\n    // TODO: Invoke some kind of callback?\n\n    // 5. Set response to the result of running HTTP-network-or-cache fetch given\n    // fetchParams.\n    // TODO\n    return makeNetworkError('proxy authentication required')\n  }\n\n  // 16. If all of the following are true\n  if (\n    // response’s status is 421\n    response.status === 421 &&\n    // isNewConnectionFetch is false\n    !isNewConnectionFetch &&\n    // request’s body is null, or request’s body is non-null and request’s body’s source is non-null\n    (request.body == null || request.body.source != null)\n  ) {\n    // then:\n\n    // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.\n    if (isCancelled(fetchParams)) {\n      return makeAppropriateNetworkError(fetchParams)\n    }\n\n    // 2. Set response to the result of running HTTP-network-or-cache\n    // fetch given fetchParams, isAuthenticationFetch, and true.\n\n    // TODO (spec): The spec doesn't specify this but we need to cancel\n    // the active response before we can start a new one.\n    // https://github.com/whatwg/fetch/issues/1293\n    fetchParams.controller.connection.destroy()\n\n    response = await httpNetworkOrCacheFetch(\n      fetchParams,\n      isAuthenticationFetch,\n      true\n    )\n  }\n\n  // 17. If isAuthenticationFetch is true, then create an authentication entry\n  if (isAuthenticationFetch) {\n    // TODO\n  }\n\n  // 18. Return response.\n  return response\n}\n\n// https://fetch.spec.whatwg.org/#http-network-fetch\nasync function httpNetworkFetch (\n  fetchParams,\n  includeCredentials = false,\n  forceNewConnection = false\n) {\n  assert(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed)\n\n  fetchParams.controller.connection = {\n    abort: null,\n    destroyed: false,\n    destroy (err, abort = true) {\n      if (!this.destroyed) {\n        this.destroyed = true\n        if (abort) {\n          this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError'))\n        }\n      }\n    }\n  }\n\n  // 1. Let request be fetchParams’s request.\n  const request = fetchParams.request\n\n  // 2. Let response be null.\n  let response = null\n\n  // 3. Let timingInfo be fetchParams’s timing info.\n  const timingInfo = fetchParams.timingInfo\n\n  // 4. Let httpCache be the result of determining the HTTP cache partition,\n  // given request.\n  // TODO: cache\n  const httpCache = null\n\n  // 5. If httpCache is null, then set request’s cache mode to \"no-store\".\n  if (httpCache == null) {\n    request.cache = 'no-store'\n  }\n\n  // 6. Let networkPartitionKey be the result of determining the network\n  // partition key given request.\n  // TODO\n\n  // 7. Let newConnection be \"yes\" if forceNewConnection is true; otherwise\n  // \"no\".\n  const newConnection = forceNewConnection ? 'yes' : 'no' // eslint-disable-line no-unused-vars\n\n  // 8. Switch on request’s mode:\n  if (request.mode === 'websocket') {\n    // Let connection be the result of obtaining a WebSocket connection,\n    // given request’s current URL.\n    // TODO\n  } else {\n    // Let connection be the result of obtaining a connection, given\n    // networkPartitionKey, request’s current URL’s origin,\n    // includeCredentials, and forceNewConnection.\n    // TODO\n  }\n\n  // 9. Run these steps, but abort when the ongoing fetch is terminated:\n\n  //    1. If connection is failure, then return a network error.\n\n  //    2. Set timingInfo’s final connection timing info to the result of\n  //    calling clamp and coarsen connection timing info with connection’s\n  //    timing info, timingInfo’s post-redirect start time, and fetchParams’s\n  //    cross-origin isolated capability.\n\n  //    3. If connection is not an HTTP/2 connection, request’s body is non-null,\n  //    and request’s body’s source is null, then append (`Transfer-Encoding`,\n  //    `chunked`) to request’s header list.\n\n  //    4. Set timingInfo’s final network-request start time to the coarsened\n  //    shared current time given fetchParams’s cross-origin isolated\n  //    capability.\n\n  //    5. Set response to the result of making an HTTP request over connection\n  //    using request with the following caveats:\n\n  //        - Follow the relevant requirements from HTTP. [HTTP] [HTTP-SEMANTICS]\n  //        [HTTP-COND] [HTTP-CACHING] [HTTP-AUTH]\n\n  //        - If request’s body is non-null, and request’s body’s source is null,\n  //        then the user agent may have a buffer of up to 64 kibibytes and store\n  //        a part of request’s body in that buffer. If the user agent reads from\n  //        request’s body beyond that buffer’s size and the user agent needs to\n  //        resend request, then instead return a network error.\n\n  //        - Set timingInfo’s final network-response start time to the coarsened\n  //        shared current time given fetchParams’s cross-origin isolated capability,\n  //        immediately after the user agent’s HTTP parser receives the first byte\n  //        of the response (e.g., frame header bytes for HTTP/2 or response status\n  //        line for HTTP/1.x).\n\n  //        - Wait until all the headers are transmitted.\n\n  //        - Any responses whose status is in the range 100 to 199, inclusive,\n  //        and is not 101, are to be ignored, except for the purposes of setting\n  //        timingInfo’s final network-response start time above.\n\n  //    - If request’s header list contains `Transfer-Encoding`/`chunked` and\n  //    response is transferred via HTTP/1.0 or older, then return a network\n  //    error.\n\n  //    - If the HTTP request results in a TLS client certificate dialog, then:\n\n  //        1. If request’s window is an environment settings object, make the\n  //        dialog available in request’s window.\n\n  //        2. Otherwise, return a network error.\n\n  // To transmit request’s body body, run these steps:\n  let requestBody = null\n  // 1. If body is null and fetchParams’s process request end-of-body is\n  // non-null, then queue a fetch task given fetchParams’s process request\n  // end-of-body and fetchParams’s task destination.\n  if (request.body == null && fetchParams.processRequestEndOfBody) {\n    queueMicrotask(() => fetchParams.processRequestEndOfBody())\n  } else if (request.body != null) {\n    // 2. Otherwise, if body is non-null:\n\n    //    1. Let processBodyChunk given bytes be these steps:\n    const processBodyChunk = async function * (bytes) {\n      // 1. If the ongoing fetch is terminated, then abort these steps.\n      if (isCancelled(fetchParams)) {\n        return\n      }\n\n      // 2. Run this step in parallel: transmit bytes.\n      yield bytes\n\n      // 3. If fetchParams’s process request body is non-null, then run\n      // fetchParams’s process request body given bytes’s length.\n      fetchParams.processRequestBodyChunkLength?.(bytes.byteLength)\n    }\n\n    // 2. Let processEndOfBody be these steps:\n    const processEndOfBody = () => {\n      // 1. If fetchParams is canceled, then abort these steps.\n      if (isCancelled(fetchParams)) {\n        return\n      }\n\n      // 2. If fetchParams’s process request end-of-body is non-null,\n      // then run fetchParams’s process request end-of-body.\n      if (fetchParams.processRequestEndOfBody) {\n        fetchParams.processRequestEndOfBody()\n      }\n    }\n\n    // 3. Let processBodyError given e be these steps:\n    const processBodyError = (e) => {\n      // 1. If fetchParams is canceled, then abort these steps.\n      if (isCancelled(fetchParams)) {\n        return\n      }\n\n      // 2. If e is an \"AbortError\" DOMException, then abort fetchParams’s controller.\n      if (e.name === 'AbortError') {\n        fetchParams.controller.abort()\n      } else {\n        fetchParams.controller.terminate(e)\n      }\n    }\n\n    // 4. Incrementally read request’s body given processBodyChunk, processEndOfBody,\n    // processBodyError, and fetchParams’s task destination.\n    requestBody = (async function * () {\n      try {\n        for await (const bytes of request.body.stream) {\n          yield * processBodyChunk(bytes)\n        }\n        processEndOfBody()\n      } catch (err) {\n        processBodyError(err)\n      }\n    })()\n  }\n\n  try {\n    // socket is only provided for websockets\n    const { body, status, statusText, headersList, socket } = await dispatch({ body: requestBody })\n\n    if (socket) {\n      response = makeResponse({ status, statusText, headersList, socket })\n    } else {\n      const iterator = body[Symbol.asyncIterator]()\n      fetchParams.controller.next = () => iterator.next()\n\n      response = makeResponse({ status, statusText, headersList })\n    }\n  } catch (err) {\n    // 10. If aborted, then:\n    if (err.name === 'AbortError') {\n      // 1. If connection uses HTTP/2, then transmit an RST_STREAM frame.\n      fetchParams.controller.connection.destroy()\n\n      // 2. Return the appropriate network error for fetchParams.\n      return makeAppropriateNetworkError(fetchParams, err)\n    }\n\n    return makeNetworkError(err)\n  }\n\n  // 11. Let pullAlgorithm be an action that resumes the ongoing fetch\n  // if it is suspended.\n  const pullAlgorithm = () => {\n    return fetchParams.controller.resume()\n  }\n\n  // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s\n  // controller with reason, given reason.\n  const cancelAlgorithm = (reason) => {\n    // If the aborted fetch was already terminated, then we do not\n    // need to do anything.\n    if (!isCancelled(fetchParams)) {\n      fetchParams.controller.abort(reason)\n    }\n  }\n\n  // 13. Let highWaterMark be a non-negative, non-NaN number, chosen by\n  // the user agent.\n  // TODO\n\n  // 14. Let sizeAlgorithm be an algorithm that accepts a chunk object\n  // and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent.\n  // TODO\n\n  // 15. Let stream be a new ReadableStream.\n  // 16. Set up stream with byte reading support with pullAlgorithm set to pullAlgorithm,\n  //     cancelAlgorithm set to cancelAlgorithm.\n  const stream = new ReadableStream(\n    {\n      start (controller) {\n        fetchParams.controller.controller = controller\n      },\n      pull: pullAlgorithm,\n      cancel: cancelAlgorithm,\n      type: 'bytes'\n    }\n  )\n\n  // 17. Run these steps, but abort when the ongoing fetch is terminated:\n\n  //    1. Set response’s body to a new body whose stream is stream.\n  response.body = { stream, source: null, length: null }\n\n  //    2. If response is not a network error and request’s cache mode is\n  //    not \"no-store\", then update response in httpCache for request.\n  //    TODO\n\n  //    3. If includeCredentials is true and the user agent is not configured\n  //    to block cookies for request (see section 7 of [COOKIES]), then run the\n  //    \"set-cookie-string\" parsing algorithm (see section 5.2 of [COOKIES]) on\n  //    the value of each header whose name is a byte-case-insensitive match for\n  //    `Set-Cookie` in response’s header list, if any, and request’s current URL.\n  //    TODO\n\n  // 18. If aborted, then:\n  // TODO\n\n  // 19. Run these steps in parallel:\n\n  //    1. Run these steps, but abort when fetchParams is canceled:\n  if (!fetchParams.controller.resume) {\n    fetchParams.controller.on('terminated', onAborted)\n  }\n\n  fetchParams.controller.resume = async () => {\n    // 1. While true\n    while (true) {\n      // 1-3. See onData...\n\n      // 4. Set bytes to the result of handling content codings given\n      // codings and bytes.\n      let bytes\n      let isFailure\n      try {\n        const { done, value } = await fetchParams.controller.next()\n\n        if (isAborted(fetchParams)) {\n          break\n        }\n\n        bytes = done ? undefined : value\n      } catch (err) {\n        if (fetchParams.controller.ended && !timingInfo.encodedBodySize) {\n          // zlib doesn't like empty streams.\n          bytes = undefined\n        } else {\n          bytes = err\n\n          // err may be propagated from the result of calling readablestream.cancel,\n          // which might not be an error. https://github.com/nodejs/undici/issues/2009\n          isFailure = true\n        }\n      }\n\n      if (bytes === undefined) {\n        // 2. Otherwise, if the bytes transmission for response’s message\n        // body is done normally and stream is readable, then close\n        // stream, finalize response for fetchParams and response, and\n        // abort these in-parallel steps.\n        readableStreamClose(fetchParams.controller.controller)\n\n        finalizeResponse(fetchParams, response)\n\n        return\n      }\n\n      // 5. Increase timingInfo’s decoded body size by bytes’s length.\n      timingInfo.decodedBodySize += bytes?.byteLength ?? 0\n\n      // 6. If bytes is failure, then terminate fetchParams’s controller.\n      if (isFailure) {\n        fetchParams.controller.terminate(bytes)\n        return\n      }\n\n      // 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes\n      // into stream.\n      const buffer = new Uint8Array(bytes)\n      if (buffer.byteLength) {\n        fetchParams.controller.controller.enqueue(buffer)\n      }\n\n      // 8. If stream is errored, then terminate the ongoing fetch.\n      if (isErrored(stream)) {\n        fetchParams.controller.terminate()\n        return\n      }\n\n      // 9. If stream doesn’t need more data ask the user agent to suspend\n      // the ongoing fetch.\n      if (fetchParams.controller.controller.desiredSize <= 0) {\n        return\n      }\n    }\n  }\n\n  //    2. If aborted, then:\n  function onAborted (reason) {\n    // 2. If fetchParams is aborted, then:\n    if (isAborted(fetchParams)) {\n      // 1. Set response’s aborted flag.\n      response.aborted = true\n\n      // 2. If stream is readable, then error stream with the result of\n      //    deserialize a serialized abort reason given fetchParams’s\n      //    controller’s serialized abort reason and an\n      //    implementation-defined realm.\n      if (isReadable(stream)) {\n        fetchParams.controller.controller.error(\n          fetchParams.controller.serializedAbortReason\n        )\n      }\n    } else {\n      // 3. Otherwise, if stream is readable, error stream with a TypeError.\n      if (isReadable(stream)) {\n        fetchParams.controller.controller.error(new TypeError('terminated', {\n          cause: isErrorLike(reason) ? reason : undefined\n        }))\n      }\n    }\n\n    // 4. If connection uses HTTP/2, then transmit an RST_STREAM frame.\n    // 5. Otherwise, the user agent should close connection unless it would be bad for performance to do so.\n    fetchParams.controller.connection.destroy()\n  }\n\n  // 20. Return response.\n  return response\n\n  function dispatch ({ body }) {\n    const url = requestCurrentURL(request)\n    /** @type {import('../../..').Agent} */\n    const agent = fetchParams.controller.dispatcher\n\n    const path = url.pathname + url.search\n    const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === '?'\n\n    return new Promise((resolve, reject) => agent.dispatch(\n      {\n        path: hasTrailingQuestionMark ? `${path}?` : path,\n        origin: url.origin,\n        method: request.method,\n        body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,\n        headers: request.headersList.entries,\n        maxRedirections: 0,\n        upgrade: request.mode === 'websocket' ? 'websocket' : undefined\n      },\n      {\n        body: null,\n        abort: null,\n\n        onConnect (abort) {\n          // TODO (fix): Do we need connection here?\n          const { connection } = fetchParams.controller\n\n          // Set timingInfo’s final connection timing info to the result of calling clamp and coarsen\n          // connection timing info with connection’s timing info, timingInfo’s post-redirect start\n          // time, and fetchParams’s cross-origin isolated capability.\n          // TODO: implement connection timing\n          timingInfo.finalConnectionTimingInfo = clampAndCoarsenConnectionTimingInfo(undefined, timingInfo.postRedirectStartTime, fetchParams.crossOriginIsolatedCapability)\n\n          if (connection.destroyed) {\n            abort(new DOMException('The operation was aborted.', 'AbortError'))\n          } else {\n            fetchParams.controller.on('terminated', abort)\n            this.abort = connection.abort = abort\n          }\n\n          // Set timingInfo’s final network-request start time to the coarsened shared current time given\n          // fetchParams’s cross-origin isolated capability.\n          timingInfo.finalNetworkRequestStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)\n        },\n\n        onResponseStarted () {\n          // Set timingInfo’s final network-response start time to the coarsened shared current\n          // time given fetchParams’s cross-origin isolated capability, immediately after the\n          // user agent’s HTTP parser receives the first byte of the response (e.g., frame header\n          // bytes for HTTP/2 or response status line for HTTP/1.x).\n          timingInfo.finalNetworkResponseStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)\n        },\n\n        onHeaders (status, rawHeaders, resume, statusText) {\n          if (status < 200) {\n            return false\n          }\n\n          const headersList = new HeadersList()\n\n          for (let i = 0; i < rawHeaders.length; i += 2) {\n            headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)\n          }\n          const location = headersList.get('location', true)\n\n          this.body = new Readable({ read: resume })\n\n          const willFollow = location && request.redirect === 'follow' &&\n            redirectStatusSet.has(status)\n\n          const decoders = []\n\n          // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding\n          if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {\n            // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1\n            const contentEncoding = headersList.get('content-encoding', true)\n            // \"All content-coding values are case-insensitive...\"\n            /** @type {string[]} */\n            const codings = contentEncoding ? contentEncoding.toLowerCase().split(',') : []\n\n            // Limit the number of content-encodings to prevent resource exhaustion.\n            // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).\n            const maxContentEncodings = 5\n            if (codings.length > maxContentEncodings) {\n              reject(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`))\n              return true\n            }\n\n            for (let i = codings.length - 1; i >= 0; --i) {\n              const coding = codings[i].trim()\n              // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2\n              if (coding === 'x-gzip' || coding === 'gzip') {\n                decoders.push(zlib.createGunzip({\n                  // Be less strict when decoding compressed responses, since sometimes\n                  // servers send slightly invalid responses that are still accepted\n                  // by common browsers.\n                  // Always using Z_SYNC_FLUSH is what cURL does.\n                  flush: zlib.constants.Z_SYNC_FLUSH,\n                  finishFlush: zlib.constants.Z_SYNC_FLUSH\n                }))\n              } else if (coding === 'deflate') {\n                decoders.push(createInflate({\n                  flush: zlib.constants.Z_SYNC_FLUSH,\n                  finishFlush: zlib.constants.Z_SYNC_FLUSH\n                }))\n              } else if (coding === 'br') {\n                decoders.push(zlib.createBrotliDecompress({\n                  flush: zlib.constants.BROTLI_OPERATION_FLUSH,\n                  finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH\n                }))\n              } else if (coding === 'zstd' && hasZstd) {\n                decoders.push(zlib.createZstdDecompress({\n                  flush: zlib.constants.ZSTD_e_continue,\n                  finishFlush: zlib.constants.ZSTD_e_end\n                }))\n              } else {\n                decoders.length = 0\n                break\n              }\n            }\n          }\n\n          const onError = this.onError.bind(this)\n\n          resolve({\n            status,\n            statusText,\n            headersList,\n            body: decoders.length\n              ? pipeline(this.body, ...decoders, (err) => {\n                if (err) {\n                  this.onError(err)\n                }\n              }).on('error', onError)\n              : this.body.on('error', onError)\n          })\n\n          return true\n        },\n\n        onData (chunk) {\n          if (fetchParams.controller.dump) {\n            return\n          }\n\n          // 1. If one or more bytes have been transmitted from response’s\n          // message body, then:\n\n          //  1. Let bytes be the transmitted bytes.\n          const bytes = chunk\n\n          //  2. Let codings be the result of extracting header list values\n          //  given `Content-Encoding` and response’s header list.\n          //  See pullAlgorithm.\n\n          //  3. Increase timingInfo’s encoded body size by bytes’s length.\n          timingInfo.encodedBodySize += bytes.byteLength\n\n          //  4. See pullAlgorithm...\n\n          return this.body.push(bytes)\n        },\n\n        onComplete () {\n          if (this.abort) {\n            fetchParams.controller.off('terminated', this.abort)\n          }\n\n          fetchParams.controller.ended = true\n\n          this.body.push(null)\n        },\n\n        onError (error) {\n          if (this.abort) {\n            fetchParams.controller.off('terminated', this.abort)\n          }\n\n          this.body?.destroy(error)\n\n          fetchParams.controller.terminate(error)\n\n          reject(error)\n        },\n\n        onRequestUpgrade (_controller, status, headers, socket) {\n          // We need to support 200 for websocket over h2 as per RFC-8441\n          // Absence of session means H1\n          if ((socket.session != null && status !== 200) || (socket.session == null && status !== 101)) {\n            return false\n          }\n\n          const headersList = new HeadersList()\n\n          for (const [name, value] of Object.entries(headers)) {\n            if (value == null) {\n              continue\n            }\n\n            const headerName = name.toLowerCase()\n\n            if (Array.isArray(value)) {\n              for (const entry of value) {\n                headersList.append(headerName, String(entry), true)\n              }\n            } else {\n              headersList.append(headerName, String(value), true)\n            }\n          }\n\n          resolve({\n            status,\n            statusText: STATUS_CODES[status],\n            headersList,\n            socket\n          })\n\n          return true\n        },\n\n        onUpgrade (status, rawHeaders, socket) {\n          // We need to support 200 for websocket over h2 as per RFC-8441\n          // Absence of session means H1\n          if ((socket.session != null && status !== 200) || (socket.session == null && status !== 101)) {\n            return false\n          }\n\n          const headersList = new HeadersList()\n\n          for (let i = 0; i < rawHeaders.length; i += 2) {\n            headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)\n          }\n\n          resolve({\n            status,\n            statusText: STATUS_CODES[status],\n            headersList,\n            socket\n          })\n\n          return true\n        }\n      }\n    ))\n  }\n}\n\nmodule.exports = {\n  fetch,\n  Fetch,\n  fetching,\n  finalizeAndReportTiming\n}\n"
  },
  {
    "path": "lib/web/fetch/request.js",
    "content": "/* globals AbortController */\n\n'use strict'\n\nconst { extractBody, mixinBody, cloneBody, bodyUnusable } = require('./body')\nconst { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')\nconst util = require('../../core/util')\nconst nodeUtil = require('node:util')\nconst {\n  isValidHTTPToken,\n  sameOrigin,\n  environmentSettingsObject\n} = require('./util')\nconst {\n  forbiddenMethodsSet,\n  corsSafeListedMethodsSet,\n  referrerPolicy,\n  requestRedirect,\n  requestMode,\n  requestCredentials,\n  requestCache,\n  requestDuplex\n} = require('./constants')\nconst { kEnumerableProperty, normalizedMethodRecordsBase, normalizedMethodRecords } = util\nconst { webidl } = require('../webidl')\nconst { URLSerializer } = require('./data-url')\nconst { kConstruct } = require('../../core/symbols')\nconst assert = require('node:assert')\nconst { getMaxListeners, setMaxListeners, defaultMaxListeners } = require('node:events')\n\nconst kAbortController = Symbol('abortController')\n\nconst requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {\n  signal.removeEventListener('abort', abort)\n})\n\nconst dependentControllerMap = new WeakMap()\n\nlet abortSignalHasEventHandlerLeakWarning\n\ntry {\n  abortSignalHasEventHandlerLeakWarning = getMaxListeners(new AbortController().signal) > 0\n} catch {\n  abortSignalHasEventHandlerLeakWarning = false\n}\n\nfunction buildAbort (acRef) {\n  return abort\n\n  function abort () {\n    const ac = acRef.deref()\n    if (ac !== undefined) {\n      // Currently, there is a problem with FinalizationRegistry.\n      // https://github.com/nodejs/node/issues/49344\n      // https://github.com/nodejs/node/issues/47748\n      // In the case of abort, the first step is to unregister from it.\n      // If the controller can refer to it, it is still registered.\n      // It will be removed in the future.\n      requestFinalizer.unregister(abort)\n\n      // Unsubscribe a listener.\n      // FinalizationRegistry will no longer be called, so this must be done.\n      this.removeEventListener('abort', abort)\n\n      ac.abort(this.reason)\n\n      const controllerList = dependentControllerMap.get(ac.signal)\n\n      if (controllerList !== undefined) {\n        if (controllerList.size !== 0) {\n          for (const ref of controllerList) {\n            const ctrl = ref.deref()\n            if (ctrl !== undefined) {\n              ctrl.abort(this.reason)\n            }\n          }\n          controllerList.clear()\n        }\n        dependentControllerMap.delete(ac.signal)\n      }\n    }\n  }\n}\n\nlet patchMethodWarning = false\n\n// https://fetch.spec.whatwg.org/#request-class\nclass Request {\n  /** @type {AbortSignal} */\n  #signal\n\n  /** @type {import('../../dispatcher/dispatcher')} */\n  #dispatcher\n\n  /** @type {Headers} */\n  #headers\n\n  #state\n\n  // https://fetch.spec.whatwg.org/#dom-request\n  constructor (input, init = undefined) {\n    webidl.util.markAsUncloneable(this)\n\n    if (input === kConstruct) {\n      return\n    }\n\n    const prefix = 'Request constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    input = webidl.converters.RequestInfo(input)\n    init = webidl.converters.RequestInit(init)\n\n    // 1. Let request be null.\n    let request = null\n\n    // 2. Let fallbackMode be null.\n    let fallbackMode = null\n\n    // 3. Let baseURL be this’s relevant settings object’s API base URL.\n    const baseUrl = environmentSettingsObject.settingsObject.baseUrl\n\n    // 4. Let signal be null.\n    let signal = null\n\n    // 5. If input is a string, then:\n    if (typeof input === 'string') {\n      this.#dispatcher = init.dispatcher\n\n      // 1. Let parsedURL be the result of parsing input with baseURL.\n      // 2. If parsedURL is failure, then throw a TypeError.\n      let parsedURL\n      try {\n        parsedURL = new URL(input, baseUrl)\n      } catch (err) {\n        throw new TypeError('Failed to parse URL from ' + input, { cause: err })\n      }\n\n      // 3. If parsedURL includes credentials, then throw a TypeError.\n      if (parsedURL.username || parsedURL.password) {\n        throw new TypeError(\n          'Request cannot be constructed from a URL that includes credentials: ' +\n            input\n        )\n      }\n\n      // 4. Set request to a new request whose URL is parsedURL.\n      request = makeRequest({ urlList: [parsedURL] })\n\n      // 5. Set fallbackMode to \"cors\".\n      fallbackMode = 'cors'\n    } else {\n      // 6. Otherwise:\n\n      // 7. Assert: input is a Request object.\n      assert(webidl.is.Request(input))\n\n      // 8. Set request to input’s request.\n      request = input.#state\n\n      // 9. Set signal to input’s signal.\n      signal = input.#signal\n\n      this.#dispatcher = init.dispatcher || input.#dispatcher\n    }\n\n    // 7. Let origin be this’s relevant settings object’s origin.\n    const origin = environmentSettingsObject.settingsObject.origin\n\n    // 8. Let window be \"client\".\n    let window = 'client'\n\n    // 9. If request’s window is an environment settings object and its origin\n    // is same origin with origin, then set window to request’s window.\n    if (\n      request.window?.constructor?.name === 'EnvironmentSettingsObject' &&\n      sameOrigin(request.window, origin)\n    ) {\n      window = request.window\n    }\n\n    // 10. If init[\"window\"] exists and is non-null, then throw a TypeError.\n    if (init.window != null) {\n      throw new TypeError(`'window' option '${window}' must be null`)\n    }\n\n    // 11. If init[\"window\"] exists, then set window to \"no-window\".\n    if ('window' in init) {\n      window = 'no-window'\n    }\n\n    // 12. Set request to a new request with the following properties:\n    request = makeRequest({\n      // URL request’s URL.\n      // undici implementation note: this is set as the first item in request's urlList in makeRequest\n      // method request’s method.\n      method: request.method,\n      // header list A copy of request’s header list.\n      // undici implementation note: headersList is cloned in makeRequest\n      headersList: request.headersList,\n      // unsafe-request flag Set.\n      unsafeRequest: request.unsafeRequest,\n      // client This’s relevant settings object.\n      client: environmentSettingsObject.settingsObject,\n      // window window.\n      window,\n      // priority request’s priority.\n      priority: request.priority,\n      // origin request’s origin. The propagation of the origin is only significant for navigation requests\n      // being handled by a service worker. In this scenario a request can have an origin that is different\n      // from the current client.\n      origin: request.origin,\n      // referrer request’s referrer.\n      referrer: request.referrer,\n      // referrer policy request’s referrer policy.\n      referrerPolicy: request.referrerPolicy,\n      // mode request’s mode.\n      mode: request.mode,\n      // credentials mode request’s credentials mode.\n      credentials: request.credentials,\n      // cache mode request’s cache mode.\n      cache: request.cache,\n      // redirect mode request’s redirect mode.\n      redirect: request.redirect,\n      // integrity metadata request’s integrity metadata.\n      integrity: request.integrity,\n      // keepalive request’s keepalive.\n      keepalive: request.keepalive,\n      // reload-navigation flag request’s reload-navigation flag.\n      reloadNavigation: request.reloadNavigation,\n      // history-navigation flag request’s history-navigation flag.\n      historyNavigation: request.historyNavigation,\n      // URL list A clone of request’s URL list.\n      urlList: [...request.urlList]\n    })\n\n    const initHasKey = Object.keys(init).length !== 0\n\n    // 13. If init is not empty, then:\n    if (initHasKey) {\n      // 1. If request’s mode is \"navigate\", then set it to \"same-origin\".\n      if (request.mode === 'navigate') {\n        request.mode = 'same-origin'\n      }\n\n      // 2. Unset request’s reload-navigation flag.\n      request.reloadNavigation = false\n\n      // 3. Unset request’s history-navigation flag.\n      request.historyNavigation = false\n\n      // 4. Set request’s origin to \"client\".\n      request.origin = 'client'\n\n      // 5. Set request’s referrer to \"client\"\n      request.referrer = 'client'\n\n      // 6. Set request’s referrer policy to the empty string.\n      request.referrerPolicy = ''\n\n      // 7. Set request’s URL to request’s current URL.\n      request.url = request.urlList[request.urlList.length - 1]\n\n      // 8. Set request’s URL list to « request’s URL ».\n      request.urlList = [request.url]\n    }\n\n    // 14. If init[\"referrer\"] exists, then:\n    if (init.referrer !== undefined) {\n      // 1. Let referrer be init[\"referrer\"].\n      const referrer = init.referrer\n\n      // 2. If referrer is the empty string, then set request’s referrer to \"no-referrer\".\n      if (referrer === '') {\n        request.referrer = 'no-referrer'\n      } else {\n        // 1. Let parsedReferrer be the result of parsing referrer with\n        // baseURL.\n        // 2. If parsedReferrer is failure, then throw a TypeError.\n        let parsedReferrer\n        try {\n          parsedReferrer = new URL(referrer, baseUrl)\n        } catch (err) {\n          throw new TypeError(`Referrer \"${referrer}\" is not a valid URL.`, { cause: err })\n        }\n\n        // 3. If one of the following is true\n        // - parsedReferrer’s scheme is \"about\" and path is the string \"client\"\n        // - parsedReferrer’s origin is not same origin with origin\n        // then set request’s referrer to \"client\".\n        if (\n          (parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') ||\n          (origin && !sameOrigin(parsedReferrer, environmentSettingsObject.settingsObject.baseUrl))\n        ) {\n          request.referrer = 'client'\n        } else {\n          // 4. Otherwise, set request’s referrer to parsedReferrer.\n          request.referrer = parsedReferrer\n        }\n      }\n    }\n\n    // 15. If init[\"referrerPolicy\"] exists, then set request’s referrer policy\n    // to it.\n    if (init.referrerPolicy !== undefined) {\n      request.referrerPolicy = init.referrerPolicy\n    }\n\n    // 16. Let mode be init[\"mode\"] if it exists, and fallbackMode otherwise.\n    let mode\n    if (init.mode !== undefined) {\n      mode = init.mode\n    } else {\n      mode = fallbackMode\n    }\n\n    // 17. If mode is \"navigate\", then throw a TypeError.\n    if (mode === 'navigate') {\n      throw webidl.errors.exception({\n        header: 'Request constructor',\n        message: 'invalid request mode navigate.'\n      })\n    }\n\n    // 18. If mode is non-null, set request’s mode to mode.\n    if (mode != null) {\n      request.mode = mode\n    }\n\n    // 19. If init[\"credentials\"] exists, then set request’s credentials mode\n    // to it.\n    if (init.credentials !== undefined) {\n      request.credentials = init.credentials\n    }\n\n    // 18. If init[\"cache\"] exists, then set request’s cache mode to it.\n    if (init.cache !== undefined) {\n      request.cache = init.cache\n    }\n\n    // 21. If request’s cache mode is \"only-if-cached\" and request’s mode is\n    // not \"same-origin\", then throw a TypeError.\n    if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {\n      throw new TypeError(\n        \"'only-if-cached' can be set only with 'same-origin' mode\"\n      )\n    }\n\n    // 22. If init[\"redirect\"] exists, then set request’s redirect mode to it.\n    if (init.redirect !== undefined) {\n      request.redirect = init.redirect\n    }\n\n    // 23. If init[\"integrity\"] exists, then set request’s integrity metadata to it.\n    if (init.integrity != null) {\n      request.integrity = String(init.integrity)\n    }\n\n    // 24. If init[\"keepalive\"] exists, then set request’s keepalive to it.\n    if (init.keepalive !== undefined) {\n      request.keepalive = Boolean(init.keepalive)\n    }\n\n    // 25. If init[\"method\"] exists, then:\n    if (init.method !== undefined) {\n      // 1. Let method be init[\"method\"].\n      let method = init.method\n\n      const mayBeNormalized = normalizedMethodRecords[method]\n\n      if (mayBeNormalized !== undefined) {\n        // Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones\n        request.method = mayBeNormalized\n      } else {\n        // 2. If method is not a method or method is a forbidden method, then\n        // throw a TypeError.\n        if (!isValidHTTPToken(method)) {\n          throw new TypeError(`'${method}' is not a valid HTTP method.`)\n        }\n\n        const upperCase = method.toUpperCase()\n\n        if (forbiddenMethodsSet.has(upperCase)) {\n          throw new TypeError(`'${method}' HTTP method is unsupported.`)\n        }\n\n        // 3. Normalize method.\n        // https://fetch.spec.whatwg.org/#concept-method-normalize\n        // Note: must be in uppercase\n        method = normalizedMethodRecordsBase[upperCase] ?? method\n\n        // 4. Set request’s method to method.\n        request.method = method\n      }\n\n      if (!patchMethodWarning && request.method === 'patch') {\n        process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', {\n          code: 'UNDICI-FETCH-patch'\n        })\n\n        patchMethodWarning = true\n      }\n    }\n\n    // 26. If init[\"signal\"] exists, then set signal to it.\n    if (init.signal !== undefined) {\n      signal = init.signal\n    }\n\n    // 27. Set this’s request to request.\n    this.#state = request\n\n    // 28. Set this’s signal to a new AbortSignal object with this’s relevant\n    // Realm.\n    // TODO: could this be simplified with AbortSignal.any\n    // (https://dom.spec.whatwg.org/#dom-abortsignal-any)\n    const ac = new AbortController()\n    this.#signal = ac.signal\n\n    // 29. If signal is not null, then make this’s signal follow signal.\n    if (signal != null) {\n      if (signal.aborted) {\n        ac.abort(signal.reason)\n      } else {\n        // Keep a strong ref to ac while request object\n        // is alive. This is needed to prevent AbortController\n        // from being prematurely garbage collected.\n        // See, https://github.com/nodejs/undici/issues/1926.\n        this[kAbortController] = ac\n\n        const acRef = new WeakRef(ac)\n        const abort = buildAbort(acRef)\n\n        // If the max amount of listeners is equal to the default, increase it\n        if (abortSignalHasEventHandlerLeakWarning && getMaxListeners(signal) === defaultMaxListeners) {\n          setMaxListeners(1500, signal)\n        }\n\n        util.addAbortListener(signal, abort)\n        // The third argument must be a registry key to be unregistered.\n        // Without it, you cannot unregister.\n        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry\n        // abort is used as the unregister key. (because it is unique)\n        requestFinalizer.register(ac, { signal, abort }, abort)\n      }\n    }\n\n    // 30. Set this’s headers to a new Headers object with this’s relevant\n    // Realm, whose header list is request’s header list and guard is\n    // \"request\".\n    this.#headers = new Headers(kConstruct)\n    setHeadersList(this.#headers, request.headersList)\n    setHeadersGuard(this.#headers, 'request')\n\n    // 31. If this’s request’s mode is \"no-cors\", then:\n    if (mode === 'no-cors') {\n      // 1. If this’s request’s method is not a CORS-safelisted method,\n      // then throw a TypeError.\n      if (!corsSafeListedMethodsSet.has(request.method)) {\n        throw new TypeError(\n          `'${request.method} is unsupported in no-cors mode.`\n        )\n      }\n\n      // 2. Set this’s headers’s guard to \"request-no-cors\".\n      setHeadersGuard(this.#headers, 'request-no-cors')\n    }\n\n    // 32. If init is not empty, then:\n    if (initHasKey) {\n      /** @type {HeadersList} */\n      const headersList = getHeadersList(this.#headers)\n      // 1. Let headers be a copy of this’s headers and its associated header\n      // list.\n      // 2. If init[\"headers\"] exists, then set headers to init[\"headers\"].\n      const headers = init.headers !== undefined ? init.headers : new HeadersList(headersList)\n\n      // 3. Empty this’s headers’s header list.\n      headersList.clear()\n\n      // 4. If headers is a Headers object, then for each header in its header\n      // list, append header’s name/header’s value to this’s headers.\n      if (headers instanceof HeadersList) {\n        for (const { name, value } of headers.rawValues()) {\n          headersList.append(name, value, false)\n        }\n        // Note: Copy the `set-cookie` meta-data.\n        headersList.cookies = headers.cookies\n      } else {\n        // 5. Otherwise, fill this’s headers with headers.\n        fillHeaders(this.#headers, headers)\n      }\n    }\n\n    // 33. Let inputBody be input’s request’s body if input is a Request\n    // object; otherwise null.\n    const inputBody = webidl.is.Request(input) ? input.#state.body : null\n\n    // 34. If either init[\"body\"] exists and is non-null or inputBody is\n    // non-null, and request’s method is `GET` or `HEAD`, then throw a\n    // TypeError.\n    if (\n      (init.body != null || inputBody != null) &&\n      (request.method === 'GET' || request.method === 'HEAD')\n    ) {\n      throw new TypeError('Request with GET/HEAD method cannot have body.')\n    }\n\n    // 35. Let initBody be null.\n    let initBody = null\n\n    // 36. If init[\"body\"] exists and is non-null, then:\n    if (init.body != null) {\n      // 1. Let Content-Type be null.\n      // 2. Set initBody and Content-Type to the result of extracting\n      // init[\"body\"], with keepalive set to request’s keepalive.\n      const [extractedBody, contentType] = extractBody(\n        init.body,\n        request.keepalive\n      )\n      initBody = extractedBody\n\n      // 3, If Content-Type is non-null and this’s headers’s header list does\n      // not contain `Content-Type`, then append `Content-Type`/Content-Type to\n      // this’s headers.\n      if (contentType && !getHeadersList(this.#headers).contains('content-type', true)) {\n        this.#headers.append('content-type', contentType, true)\n      }\n    }\n\n    // 37. Let inputOrInitBody be initBody if it is non-null; otherwise\n    // inputBody.\n    const inputOrInitBody = initBody ?? inputBody\n\n    // 38. If inputOrInitBody is non-null and inputOrInitBody’s source is\n    // null, then:\n    if (inputOrInitBody != null && inputOrInitBody.source == null) {\n      // 1. If initBody is non-null and init[\"duplex\"] does not exist,\n      //    then throw a TypeError.\n      if (initBody != null && init.duplex == null) {\n        throw new TypeError('RequestInit: duplex option is required when sending a body.')\n      }\n\n      // 2. If this’s request’s mode is neither \"same-origin\" nor \"cors\",\n      // then throw a TypeError.\n      if (request.mode !== 'same-origin' && request.mode !== 'cors') {\n        throw new TypeError(\n          'If request is made from ReadableStream, mode should be \"same-origin\" or \"cors\"'\n        )\n      }\n\n      // 3. Set this’s request’s use-CORS-preflight flag.\n      request.useCORSPreflightFlag = true\n    }\n\n    // 39. Let finalBody be inputOrInitBody.\n    let finalBody = inputOrInitBody\n\n    // 40. If initBody is null and inputBody is non-null, then:\n    if (initBody == null && inputBody != null) {\n      // 1. If input is unusable, then throw a TypeError.\n      if (bodyUnusable(input.#state)) {\n        throw new TypeError(\n          'Cannot construct a Request with a Request object that has already been used.'\n        )\n      }\n\n      // 2. Set finalBody to the result of creating a proxy for inputBody.\n      // https://streams.spec.whatwg.org/#readablestream-create-a-proxy\n      const identityTransform = new TransformStream()\n      inputBody.stream.pipeThrough(identityTransform)\n      finalBody = {\n        source: inputBody.source,\n        length: inputBody.length,\n        stream: identityTransform.readable\n      }\n    }\n\n    // 41. Set this’s request’s body to finalBody.\n    this.#state.body = finalBody\n  }\n\n  // Returns request’s HTTP method, which is \"GET\" by default.\n  get method () {\n    webidl.brandCheck(this, Request)\n\n    // The method getter steps are to return this’s request’s method.\n    return this.#state.method\n  }\n\n  // Returns the URL of request as a string.\n  get url () {\n    webidl.brandCheck(this, Request)\n\n    // The url getter steps are to return this’s request’s URL, serialized.\n    return URLSerializer(this.#state.url)\n  }\n\n  // Returns a Headers object consisting of the headers associated with request.\n  // Note that headers added in the network layer by the user agent will not\n  // be accounted for in this object, e.g., the \"Host\" header.\n  get headers () {\n    webidl.brandCheck(this, Request)\n\n    // The headers getter steps are to return this’s headers.\n    return this.#headers\n  }\n\n  // Returns the kind of resource requested by request, e.g., \"document\"\n  // or \"script\".\n  get destination () {\n    webidl.brandCheck(this, Request)\n\n    // The destination getter are to return this’s request’s destination.\n    return this.#state.destination\n  }\n\n  // Returns the referrer of request. Its value can be a same-origin URL if\n  // explicitly set in init, the empty string to indicate no referrer, and\n  // \"about:client\" when defaulting to the global’s default. This is used\n  // during fetching to determine the value of the `Referer` header of the\n  // request being made.\n  get referrer () {\n    webidl.brandCheck(this, Request)\n\n    // 1. If this’s request’s referrer is \"no-referrer\", then return the\n    // empty string.\n    if (this.#state.referrer === 'no-referrer') {\n      return ''\n    }\n\n    // 2. If this’s request’s referrer is \"client\", then return\n    // \"about:client\".\n    if (this.#state.referrer === 'client') {\n      return 'about:client'\n    }\n\n    // Return this’s request’s referrer, serialized.\n    return this.#state.referrer.toString()\n  }\n\n  // Returns the referrer policy associated with request.\n  // This is used during fetching to compute the value of the request’s\n  // referrer.\n  get referrerPolicy () {\n    webidl.brandCheck(this, Request)\n\n    // The referrerPolicy getter steps are to return this’s request’s referrer policy.\n    return this.#state.referrerPolicy\n  }\n\n  // Returns the mode associated with request, which is a string indicating\n  // whether the request will use CORS, or will be restricted to same-origin\n  // URLs.\n  get mode () {\n    webidl.brandCheck(this, Request)\n\n    // The mode getter steps are to return this’s request’s mode.\n    return this.#state.mode\n  }\n\n  // Returns the credentials mode associated with request,\n  // which is a string indicating whether credentials will be sent with the\n  // request always, never, or only when sent to a same-origin URL.\n  get credentials () {\n    webidl.brandCheck(this, Request)\n\n    // The credentials getter steps are to return this’s request’s credentials mode.\n    return this.#state.credentials\n  }\n\n  // Returns the cache mode associated with request,\n  // which is a string indicating how the request will\n  // interact with the browser’s cache when fetching.\n  get cache () {\n    webidl.brandCheck(this, Request)\n\n    // The cache getter steps are to return this’s request’s cache mode.\n    return this.#state.cache\n  }\n\n  // Returns the redirect mode associated with request,\n  // which is a string indicating how redirects for the\n  // request will be handled during fetching. A request\n  // will follow redirects by default.\n  get redirect () {\n    webidl.brandCheck(this, Request)\n\n    // The redirect getter steps are to return this’s request’s redirect mode.\n    return this.#state.redirect\n  }\n\n  // Returns request’s subresource integrity metadata, which is a\n  // cryptographic hash of the resource being fetched. Its value\n  // consists of multiple hashes separated by whitespace. [SRI]\n  get integrity () {\n    webidl.brandCheck(this, Request)\n\n    // The integrity getter steps are to return this’s request’s integrity\n    // metadata.\n    return this.#state.integrity\n  }\n\n  // Returns a boolean indicating whether or not request can outlive the\n  // global in which it was created.\n  get keepalive () {\n    webidl.brandCheck(this, Request)\n\n    // The keepalive getter steps are to return this’s request’s keepalive.\n    return this.#state.keepalive\n  }\n\n  // Returns a boolean indicating whether or not request is for a reload\n  // navigation.\n  get isReloadNavigation () {\n    webidl.brandCheck(this, Request)\n\n    // The isReloadNavigation getter steps are to return true if this’s\n    // request’s reload-navigation flag is set; otherwise false.\n    return this.#state.reloadNavigation\n  }\n\n  // Returns a boolean indicating whether or not request is for a history\n  // navigation (a.k.a. back-forward navigation).\n  get isHistoryNavigation () {\n    webidl.brandCheck(this, Request)\n\n    // The isHistoryNavigation getter steps are to return true if this’s request’s\n    // history-navigation flag is set; otherwise false.\n    return this.#state.historyNavigation\n  }\n\n  // Returns the signal associated with request, which is an AbortSignal\n  // object indicating whether or not request has been aborted, and its\n  // abort event handler.\n  get signal () {\n    webidl.brandCheck(this, Request)\n\n    // The signal getter steps are to return this’s signal.\n    return this.#signal\n  }\n\n  get body () {\n    webidl.brandCheck(this, Request)\n\n    return this.#state.body ? this.#state.body.stream : null\n  }\n\n  get bodyUsed () {\n    webidl.brandCheck(this, Request)\n\n    return !!this.#state.body && util.isDisturbed(this.#state.body.stream)\n  }\n\n  get duplex () {\n    webidl.brandCheck(this, Request)\n\n    return 'half'\n  }\n\n  // Returns a clone of request.\n  clone () {\n    webidl.brandCheck(this, Request)\n\n    // 1. If this is unusable, then throw a TypeError.\n    if (bodyUnusable(this.#state)) {\n      throw new TypeError('unusable')\n    }\n\n    // 2. Let clonedRequest be the result of cloning this’s request.\n    const clonedRequest = cloneRequest(this.#state)\n\n    // 3. Let clonedRequestObject be the result of creating a Request object,\n    // given clonedRequest, this’s headers’s guard, and this’s relevant Realm.\n    // 4. Make clonedRequestObject’s signal follow this’s signal.\n    const ac = new AbortController()\n    if (this.signal.aborted) {\n      ac.abort(this.signal.reason)\n    } else {\n      let list = dependentControllerMap.get(this.signal)\n      if (list === undefined) {\n        list = new Set()\n        dependentControllerMap.set(this.signal, list)\n      }\n      const acRef = new WeakRef(ac)\n      list.add(acRef)\n      util.addAbortListener(\n        ac.signal,\n        buildAbort(acRef)\n      )\n    }\n\n    // 4. Return clonedRequestObject.\n    return fromInnerRequest(clonedRequest, this.#dispatcher, ac.signal, getHeadersGuard(this.#headers))\n  }\n\n  [nodeUtil.inspect.custom] (depth, options) {\n    if (options.depth === null) {\n      options.depth = 2\n    }\n\n    options.colors ??= true\n\n    const properties = {\n      method: this.method,\n      url: this.url,\n      headers: this.headers,\n      destination: this.destination,\n      referrer: this.referrer,\n      referrerPolicy: this.referrerPolicy,\n      mode: this.mode,\n      credentials: this.credentials,\n      cache: this.cache,\n      redirect: this.redirect,\n      integrity: this.integrity,\n      keepalive: this.keepalive,\n      isReloadNavigation: this.isReloadNavigation,\n      isHistoryNavigation: this.isHistoryNavigation,\n      signal: this.signal\n    }\n\n    return `Request ${nodeUtil.formatWithOptions(options, properties)}`\n  }\n\n  /**\n   * @param {Request} request\n   * @param {AbortSignal} newSignal\n   */\n  static setRequestSignal (request, newSignal) {\n    request.#signal = newSignal\n    return request\n  }\n\n  /**\n   * @param {Request} request\n   */\n  static getRequestDispatcher (request) {\n    return request.#dispatcher\n  }\n\n  /**\n   * @param {Request} request\n   * @param {import('../../dispatcher/dispatcher')} newDispatcher\n   */\n  static setRequestDispatcher (request, newDispatcher) {\n    request.#dispatcher = newDispatcher\n  }\n\n  /**\n   * @param {Request} request\n   * @param {Headers} newHeaders\n   */\n  static setRequestHeaders (request, newHeaders) {\n    request.#headers = newHeaders\n  }\n\n  /**\n   * @param {Request} request\n   */\n  static getRequestState (request) {\n    return request.#state\n  }\n\n  /**\n   * @param {Request} request\n   * @param {any} newState\n   */\n  static setRequestState (request, newState) {\n    request.#state = newState\n  }\n}\n\nconst { setRequestSignal, getRequestDispatcher, setRequestDispatcher, setRequestHeaders, getRequestState, setRequestState } = Request\nReflect.deleteProperty(Request, 'setRequestSignal')\nReflect.deleteProperty(Request, 'getRequestDispatcher')\nReflect.deleteProperty(Request, 'setRequestDispatcher')\nReflect.deleteProperty(Request, 'setRequestHeaders')\nReflect.deleteProperty(Request, 'getRequestState')\nReflect.deleteProperty(Request, 'setRequestState')\n\nmixinBody(Request, getRequestState)\n\n// https://fetch.spec.whatwg.org/#requests\nfunction makeRequest (init) {\n  return {\n    method: init.method ?? 'GET',\n    localURLsOnly: init.localURLsOnly ?? false,\n    unsafeRequest: init.unsafeRequest ?? false,\n    body: init.body ?? null,\n    client: init.client ?? null,\n    reservedClient: init.reservedClient ?? null,\n    replacesClientId: init.replacesClientId ?? '',\n    window: init.window ?? 'client',\n    keepalive: init.keepalive ?? false,\n    serviceWorkers: init.serviceWorkers ?? 'all',\n    initiator: init.initiator ?? '',\n    destination: init.destination ?? '',\n    priority: init.priority ?? null,\n    origin: init.origin ?? 'client',\n    policyContainer: init.policyContainer ?? 'client',\n    referrer: init.referrer ?? 'client',\n    referrerPolicy: init.referrerPolicy ?? '',\n    mode: init.mode ?? 'no-cors',\n    useCORSPreflightFlag: init.useCORSPreflightFlag ?? false,\n    credentials: init.credentials ?? 'same-origin',\n    useCredentials: init.useCredentials ?? false,\n    cache: init.cache ?? 'default',\n    redirect: init.redirect ?? 'follow',\n    integrity: init.integrity ?? '',\n    cryptoGraphicsNonceMetadata: init.cryptoGraphicsNonceMetadata ?? '',\n    parserMetadata: init.parserMetadata ?? '',\n    reloadNavigation: init.reloadNavigation ?? false,\n    historyNavigation: init.historyNavigation ?? false,\n    userActivation: init.userActivation ?? false,\n    taintedOrigin: init.taintedOrigin ?? false,\n    redirectCount: init.redirectCount ?? 0,\n    responseTainting: init.responseTainting ?? 'basic',\n    preventNoCacheCacheControlHeaderModification: init.preventNoCacheCacheControlHeaderModification ?? false,\n    done: init.done ?? false,\n    timingAllowFailed: init.timingAllowFailed ?? false,\n    useURLCredentials: init.useURLCredentials ?? undefined,\n    traversableForUserPrompts: init.traversableForUserPrompts ?? 'client',\n    urlList: init.urlList,\n    url: init.urlList[0],\n    headersList: init.headersList\n      ? new HeadersList(init.headersList)\n      : new HeadersList()\n  }\n}\n\n// https://fetch.spec.whatwg.org/#concept-request-clone\nfunction cloneRequest (request) {\n  // To clone a request request, run these steps:\n\n  // 1. Let newRequest be a copy of request, except for its body.\n  const newRequest = makeRequest({ ...request, body: null })\n\n  // 2. If request’s body is non-null, set newRequest’s body to the\n  // result of cloning request’s body.\n  if (request.body != null) {\n    newRequest.body = cloneBody(request.body)\n  }\n\n  // 3. Return newRequest.\n  return newRequest\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#request-create\n * @param {any} innerRequest\n * @param {import('../../dispatcher/agent')} dispatcher\n * @param {AbortSignal} signal\n * @param {'request' | 'immutable' | 'request-no-cors' | 'response' | 'none'} guard\n * @returns {Request}\n */\nfunction fromInnerRequest (innerRequest, dispatcher, signal, guard) {\n  const request = new Request(kConstruct)\n  setRequestState(request, innerRequest)\n  setRequestDispatcher(request, dispatcher)\n  setRequestSignal(request, signal)\n  const headers = new Headers(kConstruct)\n  setRequestHeaders(request, headers)\n  setHeadersList(headers, innerRequest.headersList)\n  setHeadersGuard(headers, guard)\n  return request\n}\n\nObject.defineProperties(Request.prototype, {\n  method: kEnumerableProperty,\n  url: kEnumerableProperty,\n  headers: kEnumerableProperty,\n  redirect: kEnumerableProperty,\n  clone: kEnumerableProperty,\n  signal: kEnumerableProperty,\n  duplex: kEnumerableProperty,\n  destination: kEnumerableProperty,\n  body: kEnumerableProperty,\n  bodyUsed: kEnumerableProperty,\n  isHistoryNavigation: kEnumerableProperty,\n  isReloadNavigation: kEnumerableProperty,\n  keepalive: kEnumerableProperty,\n  integrity: kEnumerableProperty,\n  cache: kEnumerableProperty,\n  credentials: kEnumerableProperty,\n  attribute: kEnumerableProperty,\n  referrerPolicy: kEnumerableProperty,\n  referrer: kEnumerableProperty,\n  mode: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'Request',\n    configurable: true\n  }\n})\n\nwebidl.is.Request = webidl.util.MakeTypeAssertion(Request)\n\n/**\n * @param {*} V\n * @returns {import('../../../types/fetch').Request|string}\n *\n * @see https://fetch.spec.whatwg.org/#requestinfo\n */\nwebidl.converters.RequestInfo = function (V) {\n  if (typeof V === 'string') {\n    return webidl.converters.USVString(V)\n  }\n\n  if (webidl.is.Request(V)) {\n    return V\n  }\n\n  return webidl.converters.USVString(V)\n}\n\n/**\n * @param {*} V\n * @returns {import('../../../types/fetch').RequestInit}\n * @see https://fetch.spec.whatwg.org/#requestinit\n */\nwebidl.converters.RequestInit = webidl.dictionaryConverter([\n  {\n    key: 'method',\n    converter: webidl.converters.ByteString\n  },\n  {\n    key: 'headers',\n    converter: webidl.converters.HeadersInit\n  },\n  {\n    key: 'body',\n    converter: webidl.nullableConverter(\n      webidl.converters.BodyInit\n    )\n  },\n  {\n    key: 'referrer',\n    converter: webidl.converters.USVString\n  },\n  {\n    key: 'referrerPolicy',\n    converter: webidl.converters.DOMString,\n    // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy\n    allowedValues: referrerPolicy\n  },\n  {\n    key: 'mode',\n    converter: webidl.converters.DOMString,\n    // https://fetch.spec.whatwg.org/#concept-request-mode\n    allowedValues: requestMode\n  },\n  {\n    key: 'credentials',\n    converter: webidl.converters.DOMString,\n    // https://fetch.spec.whatwg.org/#requestcredentials\n    allowedValues: requestCredentials\n  },\n  {\n    key: 'cache',\n    converter: webidl.converters.DOMString,\n    // https://fetch.spec.whatwg.org/#requestcache\n    allowedValues: requestCache\n  },\n  {\n    key: 'redirect',\n    converter: webidl.converters.DOMString,\n    // https://fetch.spec.whatwg.org/#requestredirect\n    allowedValues: requestRedirect\n  },\n  {\n    key: 'integrity',\n    converter: webidl.converters.DOMString\n  },\n  {\n    key: 'keepalive',\n    converter: webidl.converters.boolean\n  },\n  {\n    key: 'signal',\n    converter: webidl.nullableConverter(\n      (signal) => webidl.converters.AbortSignal(\n        signal,\n        'RequestInit',\n        'signal'\n      )\n    )\n  },\n  {\n    key: 'window',\n    converter: webidl.converters.any\n  },\n  {\n    key: 'duplex',\n    converter: webidl.converters.DOMString,\n    allowedValues: requestDuplex\n  },\n  {\n    key: 'dispatcher', // undici specific option\n    converter: webidl.converters.any\n  },\n  {\n    key: 'priority',\n    converter: webidl.converters.DOMString,\n    allowedValues: ['high', 'low', 'auto'],\n    defaultValue: () => 'auto'\n  }\n])\n\nmodule.exports = {\n  Request,\n  makeRequest,\n  fromInnerRequest,\n  cloneRequest,\n  getRequestDispatcher,\n  getRequestState\n}\n"
  },
  {
    "path": "lib/web/fetch/response.js",
    "content": "'use strict'\n\nconst { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = require('./headers')\nconst { extractBody, cloneBody, mixinBody, streamRegistry, bodyUnusable } = require('./body')\nconst util = require('../../core/util')\nconst nodeUtil = require('node:util')\nconst { kEnumerableProperty } = util\nconst {\n  isValidReasonPhrase,\n  isCancelled,\n  isAborted,\n  isErrorLike,\n  environmentSettingsObject: relevantRealm\n} = require('./util')\nconst {\n  redirectStatusSet,\n  nullBodyStatus\n} = require('./constants')\nconst { webidl } = require('../webidl')\nconst { URLSerializer } = require('./data-url')\nconst { kConstruct } = require('../../core/symbols')\nconst assert = require('node:assert')\nconst { isomorphicEncode, serializeJavascriptValueToJSONString } = require('../infra')\n\nconst textEncoder = new TextEncoder('utf-8')\n\n// https://fetch.spec.whatwg.org/#response-class\nclass Response {\n  /** @type {Headers} */\n  #headers\n\n  #state\n\n  // Creates network error Response.\n  static error () {\n    // The static error() method steps are to return the result of creating a\n    // Response object, given a new network error, \"immutable\", and this’s\n    // relevant Realm.\n    const responseObject = fromInnerResponse(makeNetworkError(), 'immutable')\n\n    return responseObject\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-response-json\n  static json (data, init = undefined) {\n    webidl.argumentLengthCheck(arguments, 1, 'Response.json')\n\n    if (init !== null) {\n      init = webidl.converters.ResponseInit(init)\n    }\n\n    // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.\n    const bytes = textEncoder.encode(\n      serializeJavascriptValueToJSONString(data)\n    )\n\n    // 2. Let body be the result of extracting bytes.\n    const body = extractBody(bytes)\n\n    // 3. Let responseObject be the result of creating a Response object, given a new response,\n    //    \"response\", and this’s relevant Realm.\n    const responseObject = fromInnerResponse(makeResponse({}), 'response')\n\n    // 4. Perform initialize a response given responseObject, init, and (body, \"application/json\").\n    initializeResponse(responseObject, init, { body: body[0], type: 'application/json' })\n\n    // 5. Return responseObject.\n    return responseObject\n  }\n\n  // Creates a redirect Response that redirects to url with status status.\n  static redirect (url, status = 302) {\n    webidl.argumentLengthCheck(arguments, 1, 'Response.redirect')\n\n    url = webidl.converters.USVString(url)\n    status = webidl.converters['unsigned short'](status)\n\n    // 1. Let parsedURL be the result of parsing url with current settings\n    // object’s API base URL.\n    // 2. If parsedURL is failure, then throw a TypeError.\n    // TODO: base-URL?\n    let parsedURL\n    try {\n      parsedURL = new URL(url, relevantRealm.settingsObject.baseUrl)\n    } catch (err) {\n      throw new TypeError(`Failed to parse URL from ${url}`, { cause: err })\n    }\n\n    // 3. If status is not a redirect status, then throw a RangeError.\n    if (!redirectStatusSet.has(status)) {\n      throw new RangeError(`Invalid status code ${status}`)\n    }\n\n    // 4. Let responseObject be the result of creating a Response object,\n    // given a new response, \"immutable\", and this’s relevant Realm.\n    const responseObject = fromInnerResponse(makeResponse({}), 'immutable')\n\n    // 5. Set responseObject’s response’s status to status.\n    responseObject.#state.status = status\n\n    // 6. Let value be parsedURL, serialized and isomorphic encoded.\n    const value = isomorphicEncode(URLSerializer(parsedURL))\n\n    // 7. Append `Location`/value to responseObject’s response’s header list.\n    responseObject.#state.headersList.append('location', value, true)\n\n    // 8. Return responseObject.\n    return responseObject\n  }\n\n  // https://fetch.spec.whatwg.org/#dom-response\n  constructor (body = null, init = undefined) {\n    webidl.util.markAsUncloneable(this)\n\n    if (body === kConstruct) {\n      return\n    }\n\n    if (body !== null) {\n      body = webidl.converters.BodyInit(body, 'Response', 'body')\n    }\n\n    init = webidl.converters.ResponseInit(init)\n\n    // 1. Set this’s response to a new response.\n    this.#state = makeResponse({})\n\n    // 2. Set this’s headers to a new Headers object with this’s relevant\n    // Realm, whose header list is this’s response’s header list and guard\n    // is \"response\".\n    this.#headers = new Headers(kConstruct)\n    setHeadersGuard(this.#headers, 'response')\n    setHeadersList(this.#headers, this.#state.headersList)\n\n    // 3. Let bodyWithType be null.\n    let bodyWithType = null\n\n    // 4. If body is non-null, then set bodyWithType to the result of extracting body.\n    if (body != null) {\n      const [extractedBody, type] = extractBody(body)\n      bodyWithType = { body: extractedBody, type }\n    }\n\n    // 5. Perform initialize a response given this, init, and bodyWithType.\n    initializeResponse(this, init, bodyWithType)\n  }\n\n  // Returns response’s type, e.g., \"cors\".\n  get type () {\n    webidl.brandCheck(this, Response)\n\n    // The type getter steps are to return this’s response’s type.\n    return this.#state.type\n  }\n\n  // Returns response’s URL, if it has one; otherwise the empty string.\n  get url () {\n    webidl.brandCheck(this, Response)\n\n    const urlList = this.#state.urlList\n\n    // The url getter steps are to return the empty string if this’s\n    // response’s URL is null; otherwise this’s response’s URL,\n    // serialized with exclude fragment set to true.\n    const url = urlList[urlList.length - 1] ?? null\n\n    if (url === null) {\n      return ''\n    }\n\n    return URLSerializer(url, true)\n  }\n\n  // Returns whether response was obtained through a redirect.\n  get redirected () {\n    webidl.brandCheck(this, Response)\n\n    // The redirected getter steps are to return true if this’s response’s URL\n    // list has more than one item; otherwise false.\n    return this.#state.urlList.length > 1\n  }\n\n  // Returns response’s status.\n  get status () {\n    webidl.brandCheck(this, Response)\n\n    // The status getter steps are to return this’s response’s status.\n    return this.#state.status\n  }\n\n  // Returns whether response’s status is an ok status.\n  get ok () {\n    webidl.brandCheck(this, Response)\n\n    // The ok getter steps are to return true if this’s response’s status is an\n    // ok status; otherwise false.\n    return this.#state.status >= 200 && this.#state.status <= 299\n  }\n\n  // Returns response’s status message.\n  get statusText () {\n    webidl.brandCheck(this, Response)\n\n    // The statusText getter steps are to return this’s response’s status\n    // message.\n    return this.#state.statusText\n  }\n\n  // Returns response’s headers as Headers.\n  get headers () {\n    webidl.brandCheck(this, Response)\n\n    // The headers getter steps are to return this’s headers.\n    return this.#headers\n  }\n\n  get body () {\n    webidl.brandCheck(this, Response)\n\n    return this.#state.body ? this.#state.body.stream : null\n  }\n\n  get bodyUsed () {\n    webidl.brandCheck(this, Response)\n\n    return !!this.#state.body && util.isDisturbed(this.#state.body.stream)\n  }\n\n  // Returns a clone of response.\n  clone () {\n    webidl.brandCheck(this, Response)\n\n    // 1. If this is unusable, then throw a TypeError.\n    if (bodyUnusable(this.#state)) {\n      throw webidl.errors.exception({\n        header: 'Response.clone',\n        message: 'Body has already been consumed.'\n      })\n    }\n\n    // 2. Let clonedResponse be the result of cloning this’s response.\n    const clonedResponse = cloneResponse(this.#state)\n\n    // Note: To re-register because of a new stream.\n    // Don't set finalizers other than for fetch responses.\n    if (this.#state.urlList.length !== 0 && this.#state.body?.stream) {\n      streamRegistry.register(this, new WeakRef(this.#state.body.stream))\n    }\n\n    // 3. Return the result of creating a Response object, given\n    // clonedResponse, this’s headers’s guard, and this’s relevant Realm.\n    return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers))\n  }\n\n  [nodeUtil.inspect.custom] (depth, options) {\n    if (options.depth === null) {\n      options.depth = 2\n    }\n\n    options.colors ??= true\n\n    const properties = {\n      status: this.status,\n      statusText: this.statusText,\n      headers: this.headers,\n      body: this.body,\n      bodyUsed: this.bodyUsed,\n      ok: this.ok,\n      redirected: this.redirected,\n      type: this.type,\n      url: this.url\n    }\n\n    return `Response ${nodeUtil.formatWithOptions(options, properties)}`\n  }\n\n  /**\n   * @param {Response} response\n   */\n  static getResponseHeaders (response) {\n    return response.#headers\n  }\n\n  /**\n   * @param {Response} response\n   * @param {Headers} newHeaders\n   */\n  static setResponseHeaders (response, newHeaders) {\n    response.#headers = newHeaders\n  }\n\n  /**\n   * @param {Response} response\n   */\n  static getResponseState (response) {\n    return response.#state\n  }\n\n  /**\n   * @param {Response} response\n   * @param {any} newState\n   */\n  static setResponseState (response, newState) {\n    response.#state = newState\n  }\n}\n\nconst { getResponseHeaders, setResponseHeaders, getResponseState, setResponseState } = Response\nReflect.deleteProperty(Response, 'getResponseHeaders')\nReflect.deleteProperty(Response, 'setResponseHeaders')\nReflect.deleteProperty(Response, 'getResponseState')\nReflect.deleteProperty(Response, 'setResponseState')\n\nmixinBody(Response, getResponseState)\n\nObject.defineProperties(Response.prototype, {\n  type: kEnumerableProperty,\n  url: kEnumerableProperty,\n  status: kEnumerableProperty,\n  ok: kEnumerableProperty,\n  redirected: kEnumerableProperty,\n  statusText: kEnumerableProperty,\n  headers: kEnumerableProperty,\n  clone: kEnumerableProperty,\n  body: kEnumerableProperty,\n  bodyUsed: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'Response',\n    configurable: true\n  }\n})\n\nObject.defineProperties(Response, {\n  json: kEnumerableProperty,\n  redirect: kEnumerableProperty,\n  error: kEnumerableProperty\n})\n\n// https://fetch.spec.whatwg.org/#concept-response-clone\nfunction cloneResponse (response) {\n  // To clone a response response, run these steps:\n\n  // 1. If response is a filtered response, then return a new identical\n  // filtered response whose internal response is a clone of response’s\n  // internal response.\n  if (response.internalResponse) {\n    return filterResponse(\n      cloneResponse(response.internalResponse),\n      response.type\n    )\n  }\n\n  // 2. Let newResponse be a copy of response, except for its body.\n  const newResponse = makeResponse({ ...response, body: null })\n\n  // 3. If response’s body is non-null, then set newResponse’s body to the\n  // result of cloning response’s body.\n  if (response.body != null) {\n    newResponse.body = cloneBody(response.body)\n  }\n\n  // 4. Return newResponse.\n  return newResponse\n}\n\nfunction makeResponse (init) {\n  return {\n    aborted: false,\n    rangeRequested: false,\n    timingAllowPassed: false,\n    requestIncludesCredentials: false,\n    type: 'default',\n    status: 200,\n    timingInfo: null,\n    cacheState: '',\n    statusText: '',\n    ...init,\n    headersList: init?.headersList\n      ? new HeadersList(init?.headersList)\n      : new HeadersList(),\n    urlList: init?.urlList ? [...init.urlList] : []\n  }\n}\n\nfunction makeNetworkError (reason) {\n  const isError = isErrorLike(reason)\n  return makeResponse({\n    type: 'error',\n    status: 0,\n    error: isError\n      ? reason\n      : new Error(reason ? String(reason) : reason),\n    aborted: reason && reason.name === 'AbortError'\n  })\n}\n\n// @see https://fetch.spec.whatwg.org/#concept-network-error\nfunction isNetworkError (response) {\n  return (\n    // A network error is a response whose type is \"error\",\n    response.type === 'error' &&\n    // status is 0\n    response.status === 0\n  )\n}\n\nfunction makeFilteredResponse (response, state) {\n  state = {\n    internalResponse: response,\n    ...state\n  }\n\n  return new Proxy(response, {\n    get (target, p) {\n      return p in state ? state[p] : target[p]\n    },\n    set (target, p, value) {\n      assert(!(p in state))\n      target[p] = value\n      return true\n    }\n  })\n}\n\n// https://fetch.spec.whatwg.org/#concept-filtered-response\nfunction filterResponse (response, type) {\n  // Set response to the following filtered response with response as its\n  // internal response, depending on request’s response tainting:\n  if (type === 'basic') {\n    // A basic filtered response is a filtered response whose type is \"basic\"\n    // and header list excludes any headers in internal response’s header list\n    // whose name is a forbidden response-header name.\n\n    // Note: undici does not implement forbidden response-header names\n    return makeFilteredResponse(response, {\n      type: 'basic',\n      headersList: response.headersList\n    })\n  } else if (type === 'cors') {\n    // A CORS filtered response is a filtered response whose type is \"cors\"\n    // and header list excludes any headers in internal response’s header\n    // list whose name is not a CORS-safelisted response-header name, given\n    // internal response’s CORS-exposed header-name list.\n\n    // Note: undici does not implement CORS-safelisted response-header names\n    return makeFilteredResponse(response, {\n      type: 'cors',\n      headersList: response.headersList\n    })\n  } else if (type === 'opaque') {\n    // An opaque filtered response is a filtered response whose type is\n    // \"opaque\", URL list is the empty list, status is 0, status message\n    // is the empty byte sequence, header list is empty, and body is null.\n\n    return makeFilteredResponse(response, {\n      type: 'opaque',\n      urlList: [],\n      status: 0,\n      statusText: '',\n      body: null\n    })\n  } else if (type === 'opaqueredirect') {\n    // An opaque-redirect filtered response is a filtered response whose type\n    // is \"opaqueredirect\", status is 0, status message is the empty byte\n    // sequence, header list is empty, and body is null.\n\n    return makeFilteredResponse(response, {\n      type: 'opaqueredirect',\n      status: 0,\n      statusText: '',\n      headersList: [],\n      body: null\n    })\n  } else {\n    assert(false)\n  }\n}\n\n// https://fetch.spec.whatwg.org/#appropriate-network-error\nfunction makeAppropriateNetworkError (fetchParams, err = null) {\n  // 1. Assert: fetchParams is canceled.\n  assert(isCancelled(fetchParams))\n\n  // 2. Return an aborted network error if fetchParams is aborted;\n  // otherwise return a network error.\n  return isAborted(fetchParams)\n    ? makeNetworkError(Object.assign(new DOMException('The operation was aborted.', 'AbortError'), { cause: err }))\n    : makeNetworkError(Object.assign(new DOMException('Request was cancelled.'), { cause: err }))\n}\n\n// https://whatpr.org/fetch/1392.html#initialize-a-response\nfunction initializeResponse (response, init, body) {\n  // 1. If init[\"status\"] is not in the range 200 to 599, inclusive, then\n  //    throw a RangeError.\n  if (init.status !== null && (init.status < 200 || init.status > 599)) {\n    throw new RangeError('init[\"status\"] must be in the range of 200 to 599, inclusive.')\n  }\n\n  // 2. If init[\"statusText\"] does not match the reason-phrase token production,\n  //    then throw a TypeError.\n  if ('statusText' in init && init.statusText != null) {\n    // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2:\n    //   reason-phrase  = *( HTAB / SP / VCHAR / obs-text )\n    if (!isValidReasonPhrase(String(init.statusText))) {\n      throw new TypeError('Invalid statusText')\n    }\n  }\n\n  // 3. Set response’s response’s status to init[\"status\"].\n  if ('status' in init && init.status != null) {\n    getResponseState(response).status = init.status\n  }\n\n  // 4. Set response’s response’s status message to init[\"statusText\"].\n  if ('statusText' in init && init.statusText != null) {\n    getResponseState(response).statusText = init.statusText\n  }\n\n  // 5. If init[\"headers\"] exists, then fill response’s headers with init[\"headers\"].\n  if ('headers' in init && init.headers != null) {\n    fill(getResponseHeaders(response), init.headers)\n  }\n\n  // 6. If body was given, then:\n  if (body) {\n    // 1. If response's status is a null body status, then throw a TypeError.\n    if (nullBodyStatus.includes(response.status)) {\n      throw webidl.errors.exception({\n        header: 'Response constructor',\n        message: `Invalid response status code ${response.status}`\n      })\n    }\n\n    // 2. Set response's body to body's body.\n    getResponseState(response).body = body.body\n\n    // 3. If body's type is non-null and response's header list does not contain\n    //    `Content-Type`, then append (`Content-Type`, body's type) to response's header list.\n    if (body.type != null && !getResponseState(response).headersList.contains('content-type', true)) {\n      getResponseState(response).headersList.append('content-type', body.type, true)\n    }\n  }\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#response-create\n * @param {any} innerResponse\n * @param {'request' | 'immutable' | 'request-no-cors' | 'response' | 'none'} guard\n * @returns {Response}\n */\nfunction fromInnerResponse (innerResponse, guard) {\n  const response = new Response(kConstruct)\n  setResponseState(response, innerResponse)\n  const headers = new Headers(kConstruct)\n  setResponseHeaders(response, headers)\n  setHeadersList(headers, innerResponse.headersList)\n  setHeadersGuard(headers, guard)\n\n  // Note: If innerResponse's urlList contains a URL, it is a fetch response.\n  if (innerResponse.urlList.length !== 0 && innerResponse.body?.stream) {\n    // If the target (response) is reclaimed, the cleanup callback may be called at some point with\n    // the held value provided for it (innerResponse.body.stream). The held value can be any value:\n    // a primitive or an object, even undefined. If the held value is an object, the registry keeps\n    // a strong reference to it (so it can pass it to the cleanup callback later). Reworded from\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry\n    streamRegistry.register(response, new WeakRef(innerResponse.body.stream))\n  }\n\n  return response\n}\n\n// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit\nwebidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {\n  if (typeof V === 'string') {\n    return webidl.converters.USVString(V, prefix, name)\n  }\n\n  if (webidl.is.Blob(V)) {\n    return V\n  }\n\n  if (webidl.is.BufferSource(V)) {\n    return V\n  }\n\n  if (webidl.is.FormData(V)) {\n    return V\n  }\n\n  if (webidl.is.URLSearchParams(V)) {\n    return V\n  }\n\n  return webidl.converters.DOMString(V, prefix, name)\n}\n\n// https://fetch.spec.whatwg.org/#bodyinit\nwebidl.converters.BodyInit = function (V, prefix, argument) {\n  if (webidl.is.ReadableStream(V)) {\n    return V\n  }\n\n  // Note: the spec doesn't include async iterables,\n  // this is an undici extension.\n  if (V?.[Symbol.asyncIterator]) {\n    return V\n  }\n\n  return webidl.converters.XMLHttpRequestBodyInit(V, prefix, argument)\n}\n\nwebidl.converters.ResponseInit = webidl.dictionaryConverter([\n  {\n    key: 'status',\n    converter: webidl.converters['unsigned short'],\n    defaultValue: () => 200\n  },\n  {\n    key: 'statusText',\n    converter: webidl.converters.ByteString,\n    defaultValue: () => ''\n  },\n  {\n    key: 'headers',\n    converter: webidl.converters.HeadersInit\n  }\n])\n\nwebidl.is.Response = webidl.util.MakeTypeAssertion(Response)\n\nmodule.exports = {\n  isNetworkError,\n  makeNetworkError,\n  makeResponse,\n  makeAppropriateNetworkError,\n  filterResponse,\n  Response,\n  cloneResponse,\n  fromInnerResponse,\n  getResponseState\n}\n"
  },
  {
    "path": "lib/web/fetch/util.js",
    "content": "'use strict'\n\nconst { Transform } = require('node:stream')\nconst zlib = require('node:zlib')\nconst { redirectStatusSet, referrerPolicyTokens, badPortsSet } = require('./constants')\nconst { getGlobalOrigin } = require('./global')\nconst { collectAnHTTPQuotedString, parseMIMEType } = require('./data-url')\nconst { performance } = require('node:perf_hooks')\nconst { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')\nconst assert = require('node:assert')\nconst { isUint8Array } = require('node:util/types')\nconst { webidl } = require('../webidl')\nconst { isomorphicEncode, collectASequenceOfCodePoints, removeChars } = require('../infra')\n\nfunction responseURL (response) {\n  // https://fetch.spec.whatwg.org/#responses\n  // A response has an associated URL. It is a pointer to the last URL\n  // in response’s URL list and null if response’s URL list is empty.\n  const urlList = response.urlList\n  const length = urlList.length\n  return length === 0 ? null : urlList[length - 1].toString()\n}\n\n// https://fetch.spec.whatwg.org/#concept-response-location-url\nfunction responseLocationURL (response, requestFragment) {\n  // 1. If response’s status is not a redirect status, then return null.\n  if (!redirectStatusSet.has(response.status)) {\n    return null\n  }\n\n  // 2. Let location be the result of extracting header list values given\n  // `Location` and response’s header list.\n  let location = response.headersList.get('location', true)\n\n  // 3. If location is a header value, then set location to the result of\n  //    parsing location with response’s URL.\n  if (location !== null && isValidHeaderValue(location)) {\n    if (!isValidEncodedURL(location)) {\n      // Some websites respond location header in UTF-8 form without encoding them as ASCII\n      // and major browsers redirect them to correctly UTF-8 encoded addresses.\n      // Here, we handle that behavior in the same way.\n      location = normalizeBinaryStringToUtf8(location)\n    }\n    location = new URL(location, responseURL(response))\n  }\n\n  // 4. If location is a URL whose fragment is null, then set location’s\n  // fragment to requestFragment.\n  if (location && !location.hash) {\n    location.hash = requestFragment\n  }\n\n  // 5. Return location.\n  return location\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc1738#section-2.2\n * @param {string} url\n * @returns {boolean}\n */\nfunction isValidEncodedURL (url) {\n  for (let i = 0; i < url.length; ++i) {\n    const code = url.charCodeAt(i)\n\n    if (\n      code > 0x7E || // Non-US-ASCII + DEL\n      code < 0x20 // Control characters NUL - US\n    ) {\n      return false\n    }\n  }\n  return true\n}\n\n/**\n * If string contains non-ASCII characters, assumes it's UTF-8 encoded and decodes it.\n * Since UTF-8 is a superset of ASCII, this will work for ASCII strings as well.\n * @param {string} value\n * @returns {string}\n */\nfunction normalizeBinaryStringToUtf8 (value) {\n  return Buffer.from(value, 'binary').toString('utf8')\n}\n\n/** @returns {URL} */\nfunction requestCurrentURL (request) {\n  return request.urlList[request.urlList.length - 1]\n}\n\nfunction requestBadPort (request) {\n  // 1. Let url be request’s current URL.\n  const url = requestCurrentURL(request)\n\n  // 2. If url’s scheme is an HTTP(S) scheme and url’s port is a bad port,\n  // then return blocked.\n  if (urlIsHttpHttpsScheme(url) && badPortsSet.has(url.port)) {\n    return 'blocked'\n  }\n\n  // 3. Return allowed.\n  return 'allowed'\n}\n\nfunction isErrorLike (object) {\n  return object instanceof Error || (\n    object?.constructor?.name === 'Error' ||\n    object?.constructor?.name === 'DOMException'\n  )\n}\n\n// Check whether |statusText| is a ByteString and\n// matches the Reason-Phrase token production.\n// RFC 2616: https://tools.ietf.org/html/rfc2616\n// RFC 7230: https://tools.ietf.org/html/rfc7230\n// \"reason-phrase = *( HTAB / SP / VCHAR / obs-text )\"\n// https://github.com/chromium/chromium/blob/94.0.4604.1/third_party/blink/renderer/core/fetch/response.cc#L116\nfunction isValidReasonPhrase (statusText) {\n  for (let i = 0; i < statusText.length; ++i) {\n    const c = statusText.charCodeAt(i)\n    if (\n      !(\n        (\n          c === 0x09 || // HTAB\n          (c >= 0x20 && c <= 0x7e) || // SP / VCHAR\n          (c >= 0x80 && c <= 0xff)\n        ) // obs-text\n      )\n    ) {\n      return false\n    }\n  }\n  return true\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#header-name\n * @param {string} potentialValue\n */\nconst isValidHeaderName = isValidHTTPToken\n\n/**\n * @see https://fetch.spec.whatwg.org/#header-value\n * @param {string} potentialValue\n */\nfunction isValidHeaderValue (potentialValue) {\n  // - Has no leading or trailing HTTP tab or space bytes.\n  // - Contains no 0x00 (NUL) or HTTP newline bytes.\n  return (\n    potentialValue[0] === '\\t' ||\n    potentialValue[0] === ' ' ||\n    potentialValue[potentialValue.length - 1] === '\\t' ||\n    potentialValue[potentialValue.length - 1] === ' ' ||\n    potentialValue.includes('\\n') ||\n    potentialValue.includes('\\r') ||\n    potentialValue.includes('\\0')\n  ) === false\n}\n\n/**\n * Parse a referrer policy from a Referrer-Policy header\n * @see https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header\n */\nfunction parseReferrerPolicy (actualResponse) {\n  // 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list.\n  const policyHeader = (actualResponse.headersList.get('referrer-policy', true) ?? '').split(',')\n\n  // 2. Let policy be the empty string.\n  let policy = ''\n\n  // 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty string, then set policy to token.\n\n  // Note: As the referrer-policy can contain multiple policies\n  // separated by comma, we need to loop through all of them\n  // and pick the first valid one.\n  // Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#specify_a_fallback_policy\n  if (policyHeader.length) {\n    // The right-most policy takes precedence.\n    // The left-most policy is the fallback.\n    for (let i = policyHeader.length; i !== 0; i--) {\n      const token = policyHeader[i - 1].trim()\n      if (referrerPolicyTokens.has(token)) {\n        policy = token\n        break\n      }\n    }\n  }\n\n  // 4. Return policy.\n  return policy\n}\n\n/**\n * Given a request request and a response actualResponse, this algorithm\n * updates request’s referrer policy according to the Referrer-Policy\n * header (if any) in actualResponse.\n * @see https://w3c.github.io/webappsec-referrer-policy/#set-requests-referrer-policy-on-redirect\n * @param {import('./request').Request} request\n * @param {import('./response').Response} actualResponse\n */\nfunction setRequestReferrerPolicyOnRedirect (request, actualResponse) {\n  // 1. Let policy be the result of executing § 8.1 Parse a referrer policy\n  // from a Referrer-Policy header on actualResponse.\n  const policy = parseReferrerPolicy(actualResponse)\n\n  // 2. If policy is not the empty string, then set request’s referrer policy to policy.\n  if (policy !== '') {\n    request.referrerPolicy = policy\n  }\n}\n\n// https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check\nfunction crossOriginResourcePolicyCheck () {\n  // TODO\n  return 'allowed'\n}\n\n// https://fetch.spec.whatwg.org/#concept-cors-check\nfunction corsCheck () {\n  // TODO\n  return 'success'\n}\n\n// https://fetch.spec.whatwg.org/#concept-tao-check\nfunction TAOCheck () {\n  // TODO\n  return 'success'\n}\n\nfunction appendFetchMetadata (httpRequest) {\n  //  https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-dest-header\n  //  TODO\n\n  //  https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-mode-header\n\n  //  1. Assert: r’s url is a potentially trustworthy URL.\n  //  TODO\n\n  //  2. Let header be a Structured Header whose value is a token.\n  let header = null\n\n  //  3. Set header’s value to r’s mode.\n  header = httpRequest.mode\n\n  //  4. Set a structured field value `Sec-Fetch-Mode`/header in r’s header list.\n  httpRequest.headersList.set('sec-fetch-mode', header, true)\n\n  //  https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-site-header\n  //  TODO\n\n  //  https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-user-header\n  //  TODO\n}\n\n// https://fetch.spec.whatwg.org/#append-a-request-origin-header\nfunction appendRequestOriginHeader (request) {\n  // 1. Let serializedOrigin be the result of byte-serializing a request origin\n  //    with request.\n  // TODO: implement \"byte-serializing a request origin\"\n  let serializedOrigin = request.origin\n\n  // - \"'client' is changed to an origin during fetching.\"\n  //   This doesn't happen in undici (in most cases) because undici, by default,\n  //   has no concept of origin.\n  // - request.origin can also be set to request.client.origin (client being\n  //   an environment settings object), which is undefined without using\n  //   setGlobalOrigin.\n  if (serializedOrigin === 'client' || serializedOrigin === undefined) {\n    return\n  }\n\n  // 2. If request’s response tainting is \"cors\" or request’s mode is \"websocket\",\n  //    then append (`Origin`, serializedOrigin) to request’s header list.\n  // 3. Otherwise, if request’s method is neither `GET` nor `HEAD`, then:\n  if (request.responseTainting === 'cors' || request.mode === 'websocket') {\n    request.headersList.append('origin', serializedOrigin, true)\n  } else if (request.method !== 'GET' && request.method !== 'HEAD') {\n    // 1. Switch on request’s referrer policy:\n    switch (request.referrerPolicy) {\n      case 'no-referrer':\n        // Set serializedOrigin to `null`.\n        serializedOrigin = null\n        break\n      case 'no-referrer-when-downgrade':\n      case 'strict-origin':\n      case 'strict-origin-when-cross-origin':\n        // If request’s origin is a tuple origin, its scheme is \"https\", and\n        // request’s current URL’s scheme is not \"https\", then set\n        // serializedOrigin to `null`.\n        if (request.origin && urlHasHttpsScheme(request.origin) && !urlHasHttpsScheme(requestCurrentURL(request))) {\n          serializedOrigin = null\n        }\n        break\n      case 'same-origin':\n        // If request’s origin is not same origin with request’s current URL’s\n        // origin, then set serializedOrigin to `null`.\n        if (!sameOrigin(request, requestCurrentURL(request))) {\n          serializedOrigin = null\n        }\n        break\n      default:\n        // Do nothing.\n    }\n\n    // 2. Append (`Origin`, serializedOrigin) to request’s header list.\n    request.headersList.append('origin', serializedOrigin, true)\n  }\n}\n\n// https://w3c.github.io/hr-time/#dfn-coarsen-time\nfunction coarsenTime (timestamp, crossOriginIsolatedCapability) {\n  // TODO\n  return timestamp\n}\n\n// https://fetch.spec.whatwg.org/#clamp-and-coarsen-connection-timing-info\nfunction clampAndCoarsenConnectionTimingInfo (connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {\n  if (!connectionTimingInfo?.startTime || connectionTimingInfo.startTime < defaultStartTime) {\n    return {\n      domainLookupStartTime: defaultStartTime,\n      domainLookupEndTime: defaultStartTime,\n      connectionStartTime: defaultStartTime,\n      connectionEndTime: defaultStartTime,\n      secureConnectionStartTime: defaultStartTime,\n      ALPNNegotiatedProtocol: connectionTimingInfo?.ALPNNegotiatedProtocol\n    }\n  }\n\n  return {\n    domainLookupStartTime: coarsenTime(connectionTimingInfo.domainLookupStartTime, crossOriginIsolatedCapability),\n    domainLookupEndTime: coarsenTime(connectionTimingInfo.domainLookupEndTime, crossOriginIsolatedCapability),\n    connectionStartTime: coarsenTime(connectionTimingInfo.connectionStartTime, crossOriginIsolatedCapability),\n    connectionEndTime: coarsenTime(connectionTimingInfo.connectionEndTime, crossOriginIsolatedCapability),\n    secureConnectionStartTime: coarsenTime(connectionTimingInfo.secureConnectionStartTime, crossOriginIsolatedCapability),\n    ALPNNegotiatedProtocol: connectionTimingInfo.ALPNNegotiatedProtocol\n  }\n}\n\n// https://w3c.github.io/hr-time/#dfn-coarsened-shared-current-time\nfunction coarsenedSharedCurrentTime (crossOriginIsolatedCapability) {\n  return coarsenTime(performance.now(), crossOriginIsolatedCapability)\n}\n\n// https://fetch.spec.whatwg.org/#create-an-opaque-timing-info\nfunction createOpaqueTimingInfo (timingInfo) {\n  return {\n    startTime: timingInfo.startTime ?? 0,\n    redirectStartTime: 0,\n    redirectEndTime: 0,\n    postRedirectStartTime: timingInfo.startTime ?? 0,\n    finalServiceWorkerStartTime: 0,\n    finalNetworkResponseStartTime: 0,\n    finalNetworkRequestStartTime: 0,\n    endTime: 0,\n    encodedBodySize: 0,\n    decodedBodySize: 0,\n    finalConnectionTimingInfo: null\n  }\n}\n\n// https://html.spec.whatwg.org/multipage/origin.html#policy-container\nfunction makePolicyContainer () {\n  // Note: the fetch spec doesn't make use of embedder policy or CSP list\n  return {\n    referrerPolicy: 'strict-origin-when-cross-origin'\n  }\n}\n\n// https://html.spec.whatwg.org/multipage/origin.html#clone-a-policy-container\nfunction clonePolicyContainer (policyContainer) {\n  return {\n    referrerPolicy: policyContainer.referrerPolicy\n  }\n}\n\n/**\n * Determine request’s Referrer\n *\n * @see https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer\n */\nfunction determineRequestsReferrer (request) {\n  // Given a request request, we can determine the correct referrer information\n  // to send by examining its referrer policy as detailed in the following\n  // steps, which return either no referrer or a URL:\n\n  // 1. Let policy be request's referrer policy.\n  const policy = request.referrerPolicy\n\n  // Note: policy cannot (shouldn't) be null or an empty string.\n  assert(policy)\n\n  // 2. Let environment be request’s client.\n\n  let referrerSource = null\n\n  // 3. Switch on request’s referrer:\n\n  // \"client\"\n  if (request.referrer === 'client') {\n    // Note: node isn't a browser and doesn't implement document/iframes,\n    // so we bypass this step and replace it with our own.\n\n    const globalOrigin = getGlobalOrigin()\n\n    if (!globalOrigin || globalOrigin.origin === 'null') {\n      return 'no-referrer'\n    }\n\n    // Note: we need to clone it as it's mutated\n    referrerSource = new URL(globalOrigin)\n  // a URL\n  } else if (webidl.is.URL(request.referrer)) {\n    // Let referrerSource be request’s referrer.\n    referrerSource = request.referrer\n  }\n\n  // 4. Let request’s referrerURL be the result of stripping referrerSource for\n  //    use as a referrer.\n  let referrerURL = stripURLForReferrer(referrerSource)\n\n  // 5. Let referrerOrigin be the result of stripping referrerSource for use as\n  //    a referrer, with the origin-only flag set to true.\n  const referrerOrigin = stripURLForReferrer(referrerSource, true)\n\n  // 6. If the result of serializing referrerURL is a string whose length is\n  //    greater than 4096, set referrerURL to referrerOrigin.\n  if (referrerURL.toString().length > 4096) {\n    referrerURL = referrerOrigin\n  }\n\n  // 7. The user agent MAY alter referrerURL or referrerOrigin at this point\n  // to enforce arbitrary policy considerations in the interests of minimizing\n  // data leakage. For example, the user agent could strip the URL down to an\n  // origin, modify its host, replace it with an empty string, etc.\n\n  // 8. Execute the switch statements corresponding to the value of policy:\n  switch (policy) {\n    case 'no-referrer':\n      // Return no referrer\n      return 'no-referrer'\n    case 'origin':\n      // Return referrerOrigin\n      if (referrerOrigin != null) {\n        return referrerOrigin\n      }\n      return stripURLForReferrer(referrerSource, true)\n    case 'unsafe-url':\n      // Return referrerURL.\n      return referrerURL\n    case 'strict-origin': {\n      const currentURL = requestCurrentURL(request)\n\n      // 1. If referrerURL is a potentially trustworthy URL and request’s\n      //    current URL is not a potentially trustworthy URL, then return no\n      //    referrer.\n      if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {\n        return 'no-referrer'\n      }\n      // 2. Return referrerOrigin\n      return referrerOrigin\n    }\n    case 'strict-origin-when-cross-origin': {\n      const currentURL = requestCurrentURL(request)\n\n      // 1. If the origin of referrerURL and the origin of request’s current\n      //    URL are the same, then return referrerURL.\n      if (sameOrigin(referrerURL, currentURL)) {\n        return referrerURL\n      }\n\n      // 2. If referrerURL is a potentially trustworthy URL and request’s\n      //    current URL is not a potentially trustworthy URL, then return no\n      //    referrer.\n      if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {\n        return 'no-referrer'\n      }\n\n      // 3. Return referrerOrigin.\n      return referrerOrigin\n    }\n    case 'same-origin':\n      // 1. If the origin of referrerURL and the origin of request’s current\n      // URL are the same, then return referrerURL.\n      if (sameOrigin(request, referrerURL)) {\n        return referrerURL\n      }\n      // 2. Return no referrer.\n      return 'no-referrer'\n    case 'origin-when-cross-origin':\n      // 1. If the origin of referrerURL and the origin of request’s current\n      // URL are the same, then return referrerURL.\n      if (sameOrigin(request, referrerURL)) {\n        return referrerURL\n      }\n      // 2. Return referrerOrigin.\n      return referrerOrigin\n    case 'no-referrer-when-downgrade': {\n      const currentURL = requestCurrentURL(request)\n\n      // 1. If referrerURL is a potentially trustworthy URL and request’s\n      //    current URL is not a potentially trustworthy URL, then return no\n      //    referrer.\n      if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {\n        return 'no-referrer'\n      }\n      // 2. Return referrerURL.\n      return referrerURL\n    }\n  }\n}\n\n/**\n * Certain portions of URLs must not be included when sending a URL as the\n * value of a `Referer` header: a URLs fragment, username, and password\n * components must be stripped from the URL before it’s sent out. This\n * algorithm accepts a origin-only flag, which defaults to false. If set to\n * true, the algorithm will additionally remove the URL’s path and query\n * components, leaving only the scheme, host, and port.\n *\n * @see https://w3c.github.io/webappsec-referrer-policy/#strip-url\n * @param {URL} url\n * @param {boolean} [originOnly=false]\n */\nfunction stripURLForReferrer (url, originOnly = false) {\n  // 1. Assert: url is a URL.\n  assert(webidl.is.URL(url))\n\n  // Note: Create a new URL instance to avoid mutating the original URL.\n  url = new URL(url)\n\n  // 2. If url’s scheme is a local scheme, then return no referrer.\n  if (urlIsLocal(url)) {\n    return 'no-referrer'\n  }\n\n  // 3. Set url’s username to the empty string.\n  url.username = ''\n\n  // 4. Set url’s password to the empty string.\n  url.password = ''\n\n  // 5. Set url’s fragment to null.\n  url.hash = ''\n\n  // 6. If the origin-only flag is true, then:\n  if (originOnly === true) {\n    // 1. Set url’s path to « the empty string ».\n    url.pathname = ''\n\n    // 2. Set url’s query to null.\n    url.search = ''\n  }\n\n  // 7. Return url.\n  return url\n}\n\nconst isPotentialleTrustworthyIPv4 = RegExp.prototype.test\n  .bind(/^127\\.(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){2}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)$/)\n\nconst isPotentiallyTrustworthyIPv6 = RegExp.prototype.test\n  .bind(/^(?:(?:0{1,4}:){7}|(?:0{1,4}:){1,6}:|::)0{0,3}1$/)\n\n/**\n * Check if host matches one of the CIDR notations 127.0.0.0/8 or ::1/128.\n *\n * @param {string} origin\n * @returns {boolean}\n */\nfunction isOriginIPPotentiallyTrustworthy (origin) {\n  // IPv6\n  if (origin.includes(':')) {\n    // Remove brackets from IPv6 addresses\n    if (origin[0] === '[' && origin[origin.length - 1] === ']') {\n      origin = origin.slice(1, -1)\n    }\n    return isPotentiallyTrustworthyIPv6(origin)\n  }\n\n  // IPv4\n  return isPotentialleTrustworthyIPv4(origin)\n}\n\n/**\n * A potentially trustworthy origin is one which a user agent can generally\n * trust as delivering data securely.\n *\n * Return value `true` means `Potentially Trustworthy`.\n * Return value `false` means `Not Trustworthy`.\n *\n * @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy\n * @param {string} origin\n * @returns {boolean}\n */\nfunction isOriginPotentiallyTrustworthy (origin) {\n  // 1. If origin is an opaque origin, return \"Not Trustworthy\".\n  if (origin == null || origin === 'null') {\n    return false\n  }\n\n  // 2. Assert: origin is a tuple origin.\n  origin = new URL(origin)\n\n  // 3. If origin’s scheme is either \"https\" or \"wss\",\n  //    return \"Potentially Trustworthy\".\n  if (origin.protocol === 'https:' || origin.protocol === 'wss:') {\n    return true\n  }\n\n  // 4. If origin’s host matches one of the CIDR notations 127.0.0.0/8 or\n  // ::1/128 [RFC4632], return \"Potentially Trustworthy\".\n  if (isOriginIPPotentiallyTrustworthy(origin.hostname)) {\n    return true\n  }\n\n  // 5. If the user agent conforms to the name resolution rules in\n  //    [let-localhost-be-localhost] and one of the following is true:\n\n  //    origin’s host is \"localhost\" or \"localhost.\"\n  if (origin.hostname === 'localhost' || origin.hostname === 'localhost.') {\n    return true\n  }\n\n  //    origin’s host ends with \".localhost\" or \".localhost.\"\n  if (origin.hostname.endsWith('.localhost') || origin.hostname.endsWith('.localhost.')) {\n    return true\n  }\n\n  // 6. If origin’s scheme is \"file\", return \"Potentially Trustworthy\".\n  if (origin.protocol === 'file:') {\n    return true\n  }\n\n  // 7. If origin’s scheme component is one which the user agent considers to\n  // be authenticated, return \"Potentially Trustworthy\".\n\n  // 8. If origin has been configured as a trustworthy origin, return\n  //    \"Potentially Trustworthy\".\n\n  // 9. Return \"Not Trustworthy\".\n  return false\n}\n\n/**\n * A potentially trustworthy URL is one which either inherits context from its\n * creator (about:blank, about:srcdoc, data) or one whose origin is a\n * potentially trustworthy origin.\n *\n * Return value `true` means `Potentially Trustworthy`.\n * Return value `false` means `Not Trustworthy`.\n *\n * @see https://www.w3.org/TR/secure-contexts/#is-url-trustworthy\n * @param {URL} url\n * @returns {boolean}\n */\nfunction isURLPotentiallyTrustworthy (url) {\n  // Given a URL record (url), the following algorithm returns \"Potentially\n  // Trustworthy\" or \"Not Trustworthy\" as appropriate:\n  if (!webidl.is.URL(url)) {\n    return false\n  }\n\n  // 1. If url is \"about:blank\" or \"about:srcdoc\",\n  //    return \"Potentially Trustworthy\".\n  if (url.href === 'about:blank' || url.href === 'about:srcdoc') {\n    return true\n  }\n\n  // 2. If url’s scheme is \"data\", return \"Potentially Trustworthy\".\n  if (url.protocol === 'data:') return true\n\n  // Note: The origin of blob: URLs is the origin of the context in which they\n  // were created. Therefore, blobs created in a trustworthy origin will\n  // themselves be potentially trustworthy.\n  if (url.protocol === 'blob:') return true\n\n  // 3. Return the result of executing § 3.1 Is origin potentially trustworthy?\n  // on url’s origin.\n  return isOriginPotentiallyTrustworthy(url.origin)\n}\n\n// https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request\nfunction tryUpgradeRequestToAPotentiallyTrustworthyURL (request) {\n  // TODO\n}\n\n/**\n * @link {https://html.spec.whatwg.org/multipage/origin.html#same-origin}\n * @param {URL} A\n * @param {URL} B\n */\nfunction sameOrigin (A, B) {\n  // 1. If A and B are the same opaque origin, then return true.\n  if (A.origin === B.origin && A.origin === 'null') {\n    return true\n  }\n\n  // 2. If A and B are both tuple origins and their schemes,\n  //    hosts, and port are identical, then return true.\n  if (A.protocol === B.protocol && A.hostname === B.hostname && A.port === B.port) {\n    return true\n  }\n\n  // 3. Return false.\n  return false\n}\n\nfunction isAborted (fetchParams) {\n  return fetchParams.controller.state === 'aborted'\n}\n\nfunction isCancelled (fetchParams) {\n  return fetchParams.controller.state === 'aborted' ||\n    fetchParams.controller.state === 'terminated'\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-method-normalize\n * @param {string} method\n */\nfunction normalizeMethod (method) {\n  return normalizedMethodRecordsBase[method.toLowerCase()] ?? method\n}\n\n// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object\nconst esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))\n\n/**\n * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object\n * @param {string} name name of the instance\n * @param {((target: any) => any)} kInternalIterator\n * @param {string | number} [keyIndex]\n * @param {string | number} [valueIndex]\n */\nfunction createIterator (name, kInternalIterator, keyIndex = 0, valueIndex = 1) {\n  class FastIterableIterator {\n    /** @type {any} */\n    #target\n    /** @type {'key' | 'value' | 'key+value'} */\n    #kind\n    /** @type {number} */\n    #index\n\n    /**\n     * @see https://webidl.spec.whatwg.org/#dfn-default-iterator-object\n     * @param {unknown} target\n     * @param {'key' | 'value' | 'key+value'} kind\n     */\n    constructor (target, kind) {\n      this.#target = target\n      this.#kind = kind\n      this.#index = 0\n    }\n\n    next () {\n      // 1. Let interface be the interface for which the iterator prototype object exists.\n      // 2. Let thisValue be the this value.\n      // 3. Let object be ? ToObject(thisValue).\n      // 4. If object is a platform object, then perform a security\n      //    check, passing:\n      // 5. If object is not a default iterator object for interface,\n      //    then throw a TypeError.\n      if (typeof this !== 'object' || this === null || !(#target in this)) {\n        throw new TypeError(\n          `'next' called on an object that does not implement interface ${name} Iterator.`\n        )\n      }\n\n      // 6. Let index be object’s index.\n      // 7. Let kind be object’s kind.\n      // 8. Let values be object’s target's value pairs to iterate over.\n      const index = this.#index\n      const values = kInternalIterator(this.#target)\n\n      // 9. Let len be the length of values.\n      const len = values.length\n\n      // 10. If index is greater than or equal to len, then return\n      //     CreateIterResultObject(undefined, true).\n      if (index >= len) {\n        return {\n          value: undefined,\n          done: true\n        }\n      }\n\n      // 11. Let pair be the entry in values at index index.\n      const { [keyIndex]: key, [valueIndex]: value } = values[index]\n\n      // 12. Set object’s index to index + 1.\n      this.#index = index + 1\n\n      // 13. Return the iterator result for pair and kind.\n\n      // https://webidl.spec.whatwg.org/#iterator-result\n\n      // 1. Let result be a value determined by the value of kind:\n      let result\n      switch (this.#kind) {\n        case 'key':\n          // 1. Let idlKey be pair’s key.\n          // 2. Let key be the result of converting idlKey to an\n          //    ECMAScript value.\n          // 3. result is key.\n          result = key\n          break\n        case 'value':\n          // 1. Let idlValue be pair’s value.\n          // 2. Let value be the result of converting idlValue to\n          //    an ECMAScript value.\n          // 3. result is value.\n          result = value\n          break\n        case 'key+value':\n          // 1. Let idlKey be pair’s key.\n          // 2. Let idlValue be pair’s value.\n          // 3. Let key be the result of converting idlKey to an\n          //    ECMAScript value.\n          // 4. Let value be the result of converting idlValue to\n          //    an ECMAScript value.\n          // 5. Let array be ! ArrayCreate(2).\n          // 6. Call ! CreateDataProperty(array, \"0\", key).\n          // 7. Call ! CreateDataProperty(array, \"1\", value).\n          // 8. result is array.\n          result = [key, value]\n          break\n      }\n\n      // 2. Return CreateIterResultObject(result, false).\n      return {\n        value: result,\n        done: false\n      }\n    }\n  }\n\n  // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object\n  // @ts-ignore\n  delete FastIterableIterator.prototype.constructor\n\n  Object.setPrototypeOf(FastIterableIterator.prototype, esIteratorPrototype)\n\n  Object.defineProperties(FastIterableIterator.prototype, {\n    [Symbol.toStringTag]: {\n      writable: false,\n      enumerable: false,\n      configurable: true,\n      value: `${name} Iterator`\n    },\n    next: { writable: true, enumerable: true, configurable: true }\n  })\n\n  /**\n   * @param {unknown} target\n   * @param {'key' | 'value' | 'key+value'} kind\n   * @returns {IterableIterator<any>}\n   */\n  return function (target, kind) {\n    return new FastIterableIterator(target, kind)\n  }\n}\n\n/**\n * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object\n * @param {string} name name of the instance\n * @param {any} object class\n * @param {(target: any) => any} kInternalIterator\n * @param {string | number} [keyIndex]\n * @param {string | number} [valueIndex]\n */\nfunction iteratorMixin (name, object, kInternalIterator, keyIndex = 0, valueIndex = 1) {\n  const makeIterator = createIterator(name, kInternalIterator, keyIndex, valueIndex)\n\n  const properties = {\n    keys: {\n      writable: true,\n      enumerable: true,\n      configurable: true,\n      value: function keys () {\n        webidl.brandCheck(this, object)\n        return makeIterator(this, 'key')\n      }\n    },\n    values: {\n      writable: true,\n      enumerable: true,\n      configurable: true,\n      value: function values () {\n        webidl.brandCheck(this, object)\n        return makeIterator(this, 'value')\n      }\n    },\n    entries: {\n      writable: true,\n      enumerable: true,\n      configurable: true,\n      value: function entries () {\n        webidl.brandCheck(this, object)\n        return makeIterator(this, 'key+value')\n      }\n    },\n    forEach: {\n      writable: true,\n      enumerable: true,\n      configurable: true,\n      value: function forEach (callbackfn, thisArg = globalThis) {\n        webidl.brandCheck(this, object)\n        webidl.argumentLengthCheck(arguments, 1, `${name}.forEach`)\n        if (typeof callbackfn !== 'function') {\n          throw new TypeError(\n            `Failed to execute 'forEach' on '${name}': parameter 1 is not of type 'Function'.`\n          )\n        }\n        for (const { 0: key, 1: value } of makeIterator(this, 'key+value')) {\n          callbackfn.call(thisArg, value, key, this)\n        }\n      }\n    }\n  }\n\n  return Object.defineProperties(object.prototype, {\n    ...properties,\n    [Symbol.iterator]: {\n      writable: true,\n      enumerable: false,\n      configurable: true,\n      value: properties.entries.value\n    }\n  })\n}\n\n/**\n * @param {import('./body').ExtractBodyResult} body\n * @param {(bytes: Uint8Array) => void} processBody\n * @param {(error: Error) => void} processBodyError\n * @returns {void}\n *\n * @see https://fetch.spec.whatwg.org/#body-fully-read\n */\nfunction fullyReadBody (body, processBody, processBodyError) {\n  // 1. If taskDestination is null, then set taskDestination to\n  //    the result of starting a new parallel queue.\n\n  // 2. Let successSteps given a byte sequence bytes be to queue a\n  //    fetch task to run processBody given bytes, with taskDestination.\n  const successSteps = processBody\n\n  // 3. Let errorSteps be to queue a fetch task to run processBodyError,\n  //    with taskDestination.\n  const errorSteps = processBodyError\n\n  try {\n  // 4. Let reader be the result of getting a reader for body’s stream.\n  //    If that threw an exception, then run errorSteps with that\n  //    exception and return.\n    const reader = body.stream.getReader()\n\n    // 5. Read all bytes from reader, given successSteps and errorSteps.\n    readAllBytes(reader, successSteps, errorSteps)\n  } catch (e) {\n    errorSteps(e)\n  }\n}\n\n/**\n * @param {ReadableStreamController<Uint8Array>} controller\n */\nfunction readableStreamClose (controller) {\n  try {\n    controller.close()\n    controller.byobRequest?.respond(0)\n  } catch (err) {\n    // TODO: add comment explaining why this error occurs.\n    if (!err.message.includes('Controller is already closed') && !err.message.includes('ReadableStream is already closed')) {\n      throw err\n    }\n  }\n}\n\n/**\n * @see https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes\n * @see https://streams.spec.whatwg.org/#read-loop\n * @param {ReadableStream<Uint8Array<ArrayBuffer>>} reader\n * @param {(bytes: Uint8Array) => void} successSteps\n * @param {(error: Error) => void} failureSteps\n * @returns {Promise<void>}\n */\nasync function readAllBytes (reader, successSteps, failureSteps) {\n  try {\n    const bytes = []\n    let byteLength = 0\n\n    do {\n      const { done, value: chunk } = await reader.read()\n\n      if (done) {\n        // 1. Call successSteps with bytes.\n        successSteps(Buffer.concat(bytes, byteLength))\n        return\n      }\n\n      // 1. If chunk is not a Uint8Array object, call failureSteps\n      //    with a TypeError and abort these steps.\n      if (!isUint8Array(chunk)) {\n        failureSteps(new TypeError('Received non-Uint8Array chunk'))\n        return\n      }\n\n      // 2. Append the bytes represented by chunk to bytes.\n      bytes.push(chunk)\n      byteLength += chunk.length\n\n    // 3. Read-loop given reader, bytes, successSteps, and failureSteps.\n    } while (true)\n  } catch (e) {\n    // 1. Call failureSteps with e.\n    failureSteps(e)\n  }\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#is-local\n * @param {URL} url\n * @returns {boolean}\n */\nfunction urlIsLocal (url) {\n  assert('protocol' in url) // ensure it's a url object\n\n  const protocol = url.protocol\n\n  // A URL is local if its scheme is a local scheme.\n  // A local scheme is \"about\", \"blob\", or \"data\".\n  return protocol === 'about:' || protocol === 'blob:' || protocol === 'data:'\n}\n\n/**\n * @param {string|URL} url\n * @returns {boolean}\n */\nfunction urlHasHttpsScheme (url) {\n  return (\n    (\n      typeof url === 'string' &&\n      url[5] === ':' &&\n      url[0] === 'h' &&\n      url[1] === 't' &&\n      url[2] === 't' &&\n      url[3] === 'p' &&\n      url[4] === 's'\n    ) ||\n    url.protocol === 'https:'\n  )\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#http-scheme\n * @param {URL} url\n */\nfunction urlIsHttpHttpsScheme (url) {\n  assert('protocol' in url) // ensure it's a url object\n\n  const protocol = url.protocol\n\n  return protocol === 'http:' || protocol === 'https:'\n}\n\n/**\n * @typedef {Object} RangeHeaderValue\n * @property {number|null} rangeStartValue\n * @property {number|null} rangeEndValue\n */\n\n/**\n * @see https://fetch.spec.whatwg.org/#simple-range-header-value\n * @param {string} value\n * @param {boolean} allowWhitespace\n * @return {RangeHeaderValue|'failure'}\n */\nfunction simpleRangeHeaderValue (value, allowWhitespace) {\n  // 1. Let data be the isomorphic decoding of value.\n  // Note: isomorphic decoding takes a sequence of bytes (ie. a Uint8Array) and turns it into a string,\n  // nothing more. We obviously don't need to do that if value is a string already.\n  const data = value\n\n  // 2. If data does not start with \"bytes\", then return failure.\n  if (!data.startsWith('bytes')) {\n    return 'failure'\n  }\n\n  // 3. Let position be a position variable for data, initially pointing at the 5th code point of data.\n  const position = { position: 5 }\n\n  // 4. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space,\n  //    from data given position.\n  if (allowWhitespace) {\n    collectASequenceOfCodePoints(\n      (char) => char === '\\t' || char === ' ',\n      data,\n      position\n    )\n  }\n\n  // 5. If the code point at position within data is not U+003D (=), then return failure.\n  if (data.charCodeAt(position.position) !== 0x3D) {\n    return 'failure'\n  }\n\n  // 6. Advance position by 1.\n  position.position++\n\n  // 7. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from\n  //    data given position.\n  if (allowWhitespace) {\n    collectASequenceOfCodePoints(\n      (char) => char === '\\t' || char === ' ',\n      data,\n      position\n    )\n  }\n\n  // 8. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits,\n  //    from data given position.\n  const rangeStart = collectASequenceOfCodePoints(\n    (char) => {\n      const code = char.charCodeAt(0)\n\n      return code >= 0x30 && code <= 0x39\n    },\n    data,\n    position\n  )\n\n  // 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the\n  //    empty string; otherwise null.\n  const rangeStartValue = rangeStart.length ? Number(rangeStart) : null\n\n  // 10. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space,\n  //     from data given position.\n  if (allowWhitespace) {\n    collectASequenceOfCodePoints(\n      (char) => char === '\\t' || char === ' ',\n      data,\n      position\n    )\n  }\n\n  // 11. If the code point at position within data is not U+002D (-), then return failure.\n  if (data.charCodeAt(position.position) !== 0x2D) {\n    return 'failure'\n  }\n\n  // 12. Advance position by 1.\n  position.position++\n\n  // 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab\n  //     or space, from data given position.\n  // Note from Khafra: its the same step as in #8 again lol\n  if (allowWhitespace) {\n    collectASequenceOfCodePoints(\n      (char) => char === '\\t' || char === ' ',\n      data,\n      position\n    )\n  }\n\n  // 14. Let rangeEnd be the result of collecting a sequence of code points that are\n  //     ASCII digits, from data given position.\n  // Note from Khafra: you wouldn't guess it, but this is also the same step as #8\n  const rangeEnd = collectASequenceOfCodePoints(\n    (char) => {\n      const code = char.charCodeAt(0)\n\n      return code >= 0x30 && code <= 0x39\n    },\n    data,\n    position\n  )\n\n  // 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd\n  //     is not the empty string; otherwise null.\n  // Note from Khafra: THE SAME STEP, AGAIN!!!\n  // Note: why interpret as a decimal if we only collect ascii digits?\n  const rangeEndValue = rangeEnd.length ? Number(rangeEnd) : null\n\n  // 16. If position is not past the end of data, then return failure.\n  if (position.position < data.length) {\n    return 'failure'\n  }\n\n  // 17. If rangeEndValue and rangeStartValue are null, then return failure.\n  if (rangeEndValue === null && rangeStartValue === null) {\n    return 'failure'\n  }\n\n  // 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is\n  //     greater than rangeEndValue, then return failure.\n  // Note: ... when can they not be numbers?\n  if (rangeStartValue > rangeEndValue) {\n    return 'failure'\n  }\n\n  // 19. Return (rangeStartValue, rangeEndValue).\n  return { rangeStartValue, rangeEndValue }\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#build-a-content-range\n * @param {number} rangeStart\n * @param {number} rangeEnd\n * @param {number} fullLength\n */\nfunction buildContentRange (rangeStart, rangeEnd, fullLength) {\n  // 1. Let contentRange be `bytes `.\n  let contentRange = 'bytes '\n\n  // 2. Append rangeStart, serialized and isomorphic encoded, to contentRange.\n  contentRange += isomorphicEncode(`${rangeStart}`)\n\n  // 3. Append 0x2D (-) to contentRange.\n  contentRange += '-'\n\n  // 4. Append rangeEnd, serialized and isomorphic encoded to contentRange.\n  contentRange += isomorphicEncode(`${rangeEnd}`)\n\n  // 5. Append 0x2F (/) to contentRange.\n  contentRange += '/'\n\n  // 6. Append fullLength, serialized and isomorphic encoded to contentRange.\n  contentRange += isomorphicEncode(`${fullLength}`)\n\n  // 7. Return contentRange.\n  return contentRange\n}\n\n// A Stream, which pipes the response to zlib.createInflate() or\n// zlib.createInflateRaw() depending on the first byte of the Buffer.\n// If the lower byte of the first byte is 0x08, then the stream is\n// interpreted as a zlib stream, otherwise it's interpreted as a\n// raw deflate stream.\nclass InflateStream extends Transform {\n  #zlibOptions\n\n  /** @param {zlib.ZlibOptions} [zlibOptions] */\n  constructor (zlibOptions) {\n    super()\n    this.#zlibOptions = zlibOptions\n  }\n\n  _transform (chunk, encoding, callback) {\n    if (!this._inflateStream) {\n      if (chunk.length === 0) {\n        callback()\n        return\n      }\n      this._inflateStream = (chunk[0] & 0x0F) === 0x08\n        ? zlib.createInflate(this.#zlibOptions)\n        : zlib.createInflateRaw(this.#zlibOptions)\n\n      this._inflateStream.on('data', this.push.bind(this))\n      this._inflateStream.on('end', () => this.push(null))\n      this._inflateStream.on('error', (err) => this.destroy(err))\n    }\n\n    this._inflateStream.write(chunk, encoding, callback)\n  }\n\n  _final (callback) {\n    if (this._inflateStream) {\n      this._inflateStream.end()\n      this._inflateStream = null\n    }\n    callback()\n  }\n}\n\n/**\n * @param {zlib.ZlibOptions} [zlibOptions]\n * @returns {InflateStream}\n */\nfunction createInflate (zlibOptions) {\n  return new InflateStream(zlibOptions)\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-header-extract-mime-type\n * @param {import('./headers').HeadersList} headers\n */\nfunction extractMimeType (headers) {\n  // 1. Let charset be null.\n  let charset = null\n\n  // 2. Let essence be null.\n  let essence = null\n\n  // 3. Let mimeType be null.\n  let mimeType = null\n\n  // 4. Let values be the result of getting, decoding, and splitting `Content-Type` from headers.\n  const values = getDecodeSplit('content-type', headers)\n\n  // 5. If values is null, then return failure.\n  if (values === null) {\n    return 'failure'\n  }\n\n  // 6. For each value of values:\n  for (const value of values) {\n    // 6.1. Let temporaryMimeType be the result of parsing value.\n    const temporaryMimeType = parseMIMEType(value)\n\n    // 6.2. If temporaryMimeType is failure or its essence is \"*/*\", then continue.\n    if (temporaryMimeType === 'failure' || temporaryMimeType.essence === '*/*') {\n      continue\n    }\n\n    // 6.3. Set mimeType to temporaryMimeType.\n    mimeType = temporaryMimeType\n\n    // 6.4. If mimeType’s essence is not essence, then:\n    if (mimeType.essence !== essence) {\n      // 6.4.1. Set charset to null.\n      charset = null\n\n      // 6.4.2. If mimeType’s parameters[\"charset\"] exists, then set charset to\n      //        mimeType’s parameters[\"charset\"].\n      if (mimeType.parameters.has('charset')) {\n        charset = mimeType.parameters.get('charset')\n      }\n\n      // 6.4.3. Set essence to mimeType’s essence.\n      essence = mimeType.essence\n    } else if (!mimeType.parameters.has('charset') && charset !== null) {\n      // 6.5. Otherwise, if mimeType’s parameters[\"charset\"] does not exist, and\n      //      charset is non-null, set mimeType’s parameters[\"charset\"] to charset.\n      mimeType.parameters.set('charset', charset)\n    }\n  }\n\n  // 7. If mimeType is null, then return failure.\n  if (mimeType == null) {\n    return 'failure'\n  }\n\n  // 8. Return mimeType.\n  return mimeType\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#header-value-get-decode-and-split\n * @param {string|null} value\n */\nfunction gettingDecodingSplitting (value) {\n  // 1. Let input be the result of isomorphic decoding value.\n  const input = value\n\n  // 2. Let position be a position variable for input, initially pointing at the start of input.\n  const position = { position: 0 }\n\n  // 3. Let values be a list of strings, initially empty.\n  const values = []\n\n  // 4. Let temporaryValue be the empty string.\n  let temporaryValue = ''\n\n  // 5. While position is not past the end of input:\n  while (position.position < input.length) {\n    // 5.1. Append the result of collecting a sequence of code points that are not U+0022 (\")\n    //      or U+002C (,) from input, given position, to temporaryValue.\n    temporaryValue += collectASequenceOfCodePoints(\n      (char) => char !== '\"' && char !== ',',\n      input,\n      position\n    )\n\n    // 5.2. If position is not past the end of input, then:\n    if (position.position < input.length) {\n      // 5.2.1. If the code point at position within input is U+0022 (\"), then:\n      if (input.charCodeAt(position.position) === 0x22) {\n        // 5.2.1.1. Append the result of collecting an HTTP quoted string from input, given position, to temporaryValue.\n        temporaryValue += collectAnHTTPQuotedString(\n          input,\n          position\n        )\n\n        // 5.2.1.2. If position is not past the end of input, then continue.\n        if (position.position < input.length) {\n          continue\n        }\n      } else {\n        // 5.2.2. Otherwise:\n\n        // 5.2.2.1. Assert: the code point at position within input is U+002C (,).\n        assert(input.charCodeAt(position.position) === 0x2C)\n\n        // 5.2.2.2. Advance position by 1.\n        position.position++\n      }\n    }\n\n    // 5.3. Remove all HTTP tab or space from the start and end of temporaryValue.\n    temporaryValue = removeChars(temporaryValue, true, true, (char) => char === 0x9 || char === 0x20)\n\n    // 5.4. Append temporaryValue to values.\n    values.push(temporaryValue)\n\n    // 5.6. Set temporaryValue to the empty string.\n    temporaryValue = ''\n  }\n\n  // 6. Return values.\n  return values\n}\n\n/**\n * @see https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split\n * @param {string} name lowercase header name\n * @param {import('./headers').HeadersList} list\n */\nfunction getDecodeSplit (name, list) {\n  // 1. Let value be the result of getting name from list.\n  const value = list.get(name, true)\n\n  // 2. If value is null, then return null.\n  if (value === null) {\n    return null\n  }\n\n  // 3. Return the result of getting, decoding, and splitting value.\n  return gettingDecodingSplitting(value)\n}\n\nfunction hasAuthenticationEntry (request) {\n  return false\n}\n\n/**\n * @see https://url.spec.whatwg.org/#include-credentials\n * @param {URL} url\n */\nfunction includesCredentials (url) {\n  // A URL includes credentials if its username or password is not the empty string.\n  return !!(url.username || url.password)\n}\n\n/**\n * @see https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable\n * @param {object|string} navigable\n */\nfunction isTraversableNavigable (navigable) {\n  // TODO\n  return true\n}\n\nclass EnvironmentSettingsObjectBase {\n  get baseUrl () {\n    return getGlobalOrigin()\n  }\n\n  get origin () {\n    return this.baseUrl?.origin\n  }\n\n  policyContainer = makePolicyContainer()\n}\n\nclass EnvironmentSettingsObject {\n  settingsObject = new EnvironmentSettingsObjectBase()\n}\n\nconst environmentSettingsObject = new EnvironmentSettingsObject()\n\nmodule.exports = {\n  isAborted,\n  isCancelled,\n  isValidEncodedURL,\n  ReadableStreamFrom,\n  tryUpgradeRequestToAPotentiallyTrustworthyURL,\n  clampAndCoarsenConnectionTimingInfo,\n  coarsenedSharedCurrentTime,\n  determineRequestsReferrer,\n  makePolicyContainer,\n  clonePolicyContainer,\n  appendFetchMetadata,\n  appendRequestOriginHeader,\n  TAOCheck,\n  corsCheck,\n  crossOriginResourcePolicyCheck,\n  createOpaqueTimingInfo,\n  setRequestReferrerPolicyOnRedirect,\n  isValidHTTPToken,\n  requestBadPort,\n  requestCurrentURL,\n  responseURL,\n  responseLocationURL,\n  isURLPotentiallyTrustworthy,\n  isValidReasonPhrase,\n  sameOrigin,\n  normalizeMethod,\n  iteratorMixin,\n  createIterator,\n  isValidHeaderName,\n  isValidHeaderValue,\n  isErrorLike,\n  fullyReadBody,\n  readableStreamClose,\n  urlIsLocal,\n  urlHasHttpsScheme,\n  urlIsHttpHttpsScheme,\n  readAllBytes,\n  simpleRangeHeaderValue,\n  buildContentRange,\n  createInflate,\n  extractMimeType,\n  getDecodeSplit,\n  environmentSettingsObject,\n  isOriginIPPotentiallyTrustworthy,\n  hasAuthenticationEntry,\n  includesCredentials,\n  isTraversableNavigable\n}\n"
  },
  {
    "path": "lib/web/infra/index.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { utf8DecodeBytes } = require('../../encoding')\n\n/**\n * @param {(char: string) => boolean} condition\n * @param {string} input\n * @param {{ position: number }} position\n * @returns {string}\n *\n * @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points\n */\nfunction collectASequenceOfCodePoints (condition, input, position) {\n  // 1. Let result be the empty string.\n  let result = ''\n\n  // 2. While position doesn’t point past the end of input and the\n  // code point at position within input meets the condition condition:\n  while (position.position < input.length && condition(input[position.position])) {\n    // 1. Append that code point to the end of result.\n    result += input[position.position]\n\n    // 2. Advance position by 1.\n    position.position++\n  }\n\n  // 3. Return result.\n  return result\n}\n\n/**\n * A faster collectASequenceOfCodePoints that only works when comparing a single character.\n * @param {string} char\n * @param {string} input\n * @param {{ position: number }} position\n * @returns {string}\n *\n * @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points\n */\nfunction collectASequenceOfCodePointsFast (char, input, position) {\n  const idx = input.indexOf(char, position.position)\n  const start = position.position\n\n  if (idx === -1) {\n    position.position = input.length\n    return input.slice(start)\n  }\n\n  position.position = idx\n  return input.slice(start, position.position)\n}\n\nconst ASCII_WHITESPACE_REPLACE_REGEX = /[\\u0009\\u000A\\u000C\\u000D\\u0020]/g // eslint-disable-line no-control-regex\n\n/**\n * @param {string} data\n * @returns {Uint8Array | 'failure'}\n *\n * @see https://infra.spec.whatwg.org/#forgiving-base64-decode\n */\nfunction forgivingBase64 (data) {\n  // 1. Remove all ASCII whitespace from data.\n  data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')\n\n  let dataLength = data.length\n  // 2. If data’s code point length divides by 4 leaving\n  // no remainder, then:\n  if (dataLength % 4 === 0) {\n    // 1. If data ends with one or two U+003D (=) code points,\n    // then remove them from data.\n    if (data.charCodeAt(dataLength - 1) === 0x003D) {\n      --dataLength\n      if (data.charCodeAt(dataLength - 1) === 0x003D) {\n        --dataLength\n      }\n    }\n  }\n\n  // 3. If data’s code point length divides by 4 leaving\n  // a remainder of 1, then return failure.\n  if (dataLength % 4 === 1) {\n    return 'failure'\n  }\n\n  // 4. If data contains a code point that is not one of\n  //  U+002B (+)\n  //  U+002F (/)\n  //  ASCII alphanumeric\n  // then return failure.\n  if (/[^+/0-9A-Za-z]/.test(data.length === dataLength ? data : data.substring(0, dataLength))) {\n    return 'failure'\n  }\n\n  const buffer = Buffer.from(data, 'base64')\n  return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n}\n\n/**\n * @param {number} char\n * @returns {boolean}\n *\n * @see https://infra.spec.whatwg.org/#ascii-whitespace\n */\nfunction isASCIIWhitespace (char) {\n  return (\n    char === 0x09 || // \\t\n    char === 0x0a || // \\n\n    char === 0x0c || // \\f\n    char === 0x0d || // \\r\n    char === 0x20    // space\n  )\n}\n\n/**\n * @param {Uint8Array} input\n * @returns {string}\n *\n * @see https://infra.spec.whatwg.org/#isomorphic-decode\n */\nfunction isomorphicDecode (input) {\n  // 1. To isomorphic decode a byte sequence input, return a string whose code point\n  //    length is equal to input’s length and whose code points have the same values\n  //    as the values of input’s bytes, in the same order.\n  const length = input.length\n  if ((2 << 15) - 1 > length) {\n    return String.fromCharCode.apply(null, input)\n  }\n  let result = ''\n  let i = 0\n  let addition = (2 << 15) - 1\n  while (i < length) {\n    if (i + addition > length) {\n      addition = length - i\n    }\n    result += String.fromCharCode.apply(null, input.subarray(i, i += addition))\n  }\n  return result\n}\n\nconst invalidIsomorphicEncodeValueRegex = /[^\\x00-\\xFF]/ // eslint-disable-line no-control-regex\n\n/**\n * @param {string} input\n * @returns {string}\n *\n * @see https://infra.spec.whatwg.org/#isomorphic-encode\n */\nfunction isomorphicEncode (input) {\n  // 1. Assert: input contains no code points greater than U+00FF.\n  assert(!invalidIsomorphicEncodeValueRegex.test(input))\n\n  // 2. Return a byte sequence whose length is equal to input’s code\n  //    point length and whose bytes have the same values as the\n  //    values of input’s code points, in the same order\n  return input\n}\n\n/**\n * @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value\n * @param {Uint8Array} bytes\n */\nfunction parseJSONFromBytes (bytes) {\n  return JSON.parse(utf8DecodeBytes(bytes))\n}\n\n/**\n * @param {string} str\n * @param {boolean} [leading=true]\n * @param {boolean} [trailing=true]\n * @returns {string}\n *\n * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace\n */\nfunction removeASCIIWhitespace (str, leading = true, trailing = true) {\n  return removeChars(str, leading, trailing, isASCIIWhitespace)\n}\n\n/**\n * @param {string} str\n * @param {boolean} leading\n * @param {boolean} trailing\n * @param {(charCode: number) => boolean} predicate\n * @returns {string}\n */\nfunction removeChars (str, leading, trailing, predicate) {\n  let lead = 0\n  let trail = str.length - 1\n\n  if (leading) {\n    while (lead < str.length && predicate(str.charCodeAt(lead))) lead++\n  }\n\n  if (trailing) {\n    while (trail > 0 && predicate(str.charCodeAt(trail))) trail--\n  }\n\n  return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)\n}\n\n// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string\nfunction serializeJavascriptValueToJSONString (value) {\n  // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »).\n  const result = JSON.stringify(value)\n\n  // 2. If result is undefined, then throw a TypeError.\n  if (result === undefined) {\n    throw new TypeError('Value is not JSON serializable')\n  }\n\n  // 3. Assert: result is a string.\n  assert(typeof result === 'string')\n\n  // 4. Return result.\n  return result\n}\n\nmodule.exports = {\n  collectASequenceOfCodePoints,\n  collectASequenceOfCodePointsFast,\n  forgivingBase64,\n  isASCIIWhitespace,\n  isomorphicDecode,\n  isomorphicEncode,\n  parseJSONFromBytes,\n  removeASCIIWhitespace,\n  removeChars,\n  serializeJavascriptValueToJSONString\n}\n"
  },
  {
    "path": "lib/web/subresource-integrity/Readme.md",
    "content": "# Subresource Integrity\n\nbased on Editor’s Draft, 12 June 2025\n\nThis module provides support for Subresource Integrity (SRI) in the context of web fetch operations. SRI is a security feature that allows clients to verify that fetched resources are delivered without unexpected manipulation.\n\n## Links\n\n- [Subresource Integrity](https://w3c.github.io/webappsec-subresource-integrity/)"
  },
  {
    "path": "lib/web/subresource-integrity/subresource-integrity.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { runtimeFeatures } = require('../../util/runtime-features.js')\n\n/**\n * @typedef {object} Metadata\n * @property {SRIHashAlgorithm} alg - The algorithm used for the hash.\n * @property {string} val - The base64-encoded hash value.\n */\n\n/**\n * @typedef {Metadata[]} MetadataList\n */\n\n/**\n * @typedef {('sha256' | 'sha384' | 'sha512')} SRIHashAlgorithm\n */\n\n/**\n * @type {Map<SRIHashAlgorithm, number>}\n *\n * The valid SRI hash algorithm token set is the ordered set « \"sha256\",\n * \"sha384\", \"sha512\" » (corresponding to SHA-256, SHA-384, and SHA-512\n * respectively). The ordering of this set is meaningful, with stronger\n * algorithms appearing later in the set.\n *\n * @see https://w3c.github.io/webappsec-subresource-integrity/#valid-sri-hash-algorithm-token-set\n */\nconst validSRIHashAlgorithmTokenSet = new Map([['sha256', 0], ['sha384', 1], ['sha512', 2]])\n\n// https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable\n/** @type {import('node:crypto')} */\nlet crypto\n\nif (runtimeFeatures.has('crypto')) {\n  crypto = require('node:crypto')\n  const cryptoHashes = crypto.getHashes()\n\n  // If no hashes are available, we cannot support SRI.\n  if (cryptoHashes.length === 0) {\n    validSRIHashAlgorithmTokenSet.clear()\n  }\n\n  for (const algorithm of validSRIHashAlgorithmTokenSet.keys()) {\n    // If the algorithm is not supported, remove it from the list.\n    if (cryptoHashes.includes(algorithm) === false) {\n      validSRIHashAlgorithmTokenSet.delete(algorithm)\n    }\n  }\n} else {\n  // If crypto is not available, we cannot support SRI.\n  validSRIHashAlgorithmTokenSet.clear()\n}\n\n/**\n * @typedef GetSRIHashAlgorithmIndex\n * @type {(algorithm: SRIHashAlgorithm) => number}\n * @param {SRIHashAlgorithm} algorithm\n * @returns {number} The index of the algorithm in the valid SRI hash algorithm\n * token set.\n */\n\nconst getSRIHashAlgorithmIndex = /** @type {GetSRIHashAlgorithmIndex} */ (Map.prototype.get.bind(\n  validSRIHashAlgorithmTokenSet))\n\n/**\n * @typedef IsValidSRIHashAlgorithm\n * @type {(algorithm: string) => algorithm is SRIHashAlgorithm}\n * @param {*} algorithm\n * @returns {algorithm is SRIHashAlgorithm}\n */\n\nconst isValidSRIHashAlgorithm = /** @type {IsValidSRIHashAlgorithm} */ (\n  Map.prototype.has.bind(validSRIHashAlgorithmTokenSet)\n)\n\n/**\n * @param {Uint8Array} bytes\n * @param {string} metadataList\n * @returns {boolean}\n *\n * @see https://w3c.github.io/webappsec-subresource-integrity/#does-response-match-metadatalist\n */\nconst bytesMatch = runtimeFeatures.has('crypto') === false || validSRIHashAlgorithmTokenSet.size === 0\n  // If node is not built with OpenSSL support, we cannot check\n  // a request's integrity, so allow it by default (the spec will\n  // allow requests if an invalid hash is given, as precedence).\n  ? () => true\n  : (bytes, metadataList) => {\n    // 1. Let parsedMetadata be the result of parsing metadataList.\n      const parsedMetadata = parseMetadata(metadataList)\n\n      // 2. If parsedMetadata is empty set, return true.\n      if (parsedMetadata.length === 0) {\n        return true\n      }\n\n      // 3. Let metadata be the result of getting the strongest\n      //    metadata from parsedMetadata.\n      const metadata = getStrongestMetadata(parsedMetadata)\n\n      // 4. For each item in metadata:\n      for (const item of metadata) {\n      // 1. Let algorithm be the item[\"alg\"].\n        const algorithm = item.alg\n\n        // 2. Let expectedValue be the item[\"val\"].\n        const expectedValue = item.val\n\n        // See https://github.com/web-platform-tests/wpt/commit/e4c5cc7a5e48093220528dfdd1c4012dc3837a0e\n        // \"be liberal with padding\". This is annoying, and it's not even in the spec.\n\n        // 3. Let actualValue be the result of applying algorithm to bytes .\n        const actualValue = applyAlgorithmToBytes(algorithm, bytes)\n\n        // 4. If actualValue is a case-sensitive match for expectedValue,\n        //    return true.\n        if (caseSensitiveMatch(actualValue, expectedValue)) {\n          return true\n        }\n      }\n\n      // 5. Return false.\n      return false\n    }\n\n/**\n * @param {MetadataList} metadataList\n * @returns {MetadataList} The strongest hash algorithm from the metadata list.\n */\nfunction getStrongestMetadata (metadataList) {\n  // 1. Let result be the empty set and strongest be the empty string.\n  const result = []\n  /** @type {Metadata|null} */\n  let strongest = null\n\n  // 2. For each item in set:\n  for (const item of metadataList) {\n    // 1. Assert: item[\"alg\"] is a valid SRI hash algorithm token.\n    assert(isValidSRIHashAlgorithm(item.alg), 'Invalid SRI hash algorithm token')\n\n    // 2. If result is the empty set, then:\n    if (result.length === 0) {\n      // 1. Append item to result.\n      result.push(item)\n\n      // 2. Set strongest to item.\n      strongest = item\n\n      // 3. Continue.\n      continue\n    }\n\n    // 3. Let currentAlgorithm be strongest[\"alg\"], and currentAlgorithmIndex be\n    // the index of currentAlgorithm in the valid SRI hash algorithm token set.\n    const currentAlgorithm = /** @type {Metadata} */ (strongest).alg\n    const currentAlgorithmIndex = getSRIHashAlgorithmIndex(currentAlgorithm)\n\n    // 4. Let newAlgorithm be the item[\"alg\"], and newAlgorithmIndex be the\n    // index of newAlgorithm in the valid SRI hash algorithm token set.\n    const newAlgorithm = item.alg\n    const newAlgorithmIndex = getSRIHashAlgorithmIndex(newAlgorithm)\n\n    // 5. If newAlgorithmIndex is less than currentAlgorithmIndex, then continue.\n    if (newAlgorithmIndex < currentAlgorithmIndex) {\n      continue\n\n    // 6. Otherwise, if newAlgorithmIndex is greater than\n    // currentAlgorithmIndex:\n    } else if (newAlgorithmIndex > currentAlgorithmIndex) {\n      // 1. Set strongest to item.\n      strongest = item\n\n      // 2. Set result to « item ».\n      result[0] = item\n      result.length = 1\n\n    // 7. Otherwise, newAlgorithmIndex and currentAlgorithmIndex are the same\n    // value. Append item to result.\n    } else {\n      result.push(item)\n    }\n  }\n\n  // 3. Return result.\n  return result\n}\n\n/**\n * @param {string} metadata\n * @returns {MetadataList}\n *\n * @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata\n */\nfunction parseMetadata (metadata) {\n  // 1. Let result be the empty set.\n  /** @type {MetadataList} */\n  const result = []\n\n  // 2. For each item returned by splitting metadata on spaces:\n  for (const item of metadata.split(' ')) {\n    // 1. Let expression-and-options be the result of splitting item on U+003F (?).\n    const expressionAndOptions = item.split('?', 1)\n\n    // 2. Let algorithm-expression be expression-and-options[0].\n    const algorithmExpression = expressionAndOptions[0]\n\n    // 3. Let base64-value be the empty string.\n    let base64Value = ''\n\n    // 4. Let algorithm-and-value be the result of splitting algorithm-expression on U+002D (-).\n    const algorithmAndValue = [algorithmExpression.slice(0, 6), algorithmExpression.slice(7)]\n\n    // 5. Let algorithm be algorithm-and-value[0].\n    const algorithm = algorithmAndValue[0]\n\n    // 6. If algorithm is not a valid SRI hash algorithm token, then continue.\n    if (!isValidSRIHashAlgorithm(algorithm)) {\n      continue\n    }\n\n    // 7. If algorithm-and-value[1] exists, set base64-value to\n    // algorithm-and-value[1].\n    if (algorithmAndValue[1]) {\n      base64Value = algorithmAndValue[1]\n    }\n\n    // 8. Let metadata be the ordered map\n    // «[\"alg\" → algorithm, \"val\" → base64-value]».\n    const metadata = {\n      alg: algorithm,\n      val: base64Value\n    }\n\n    // 9. Append metadata to result.\n    result.push(metadata)\n  }\n\n  // 3. Return result.\n  return result\n}\n\n/**\n * Applies the specified hash algorithm to the given bytes\n *\n * @typedef {(algorithm: SRIHashAlgorithm, bytes: Uint8Array) => string} ApplyAlgorithmToBytes\n * @param {SRIHashAlgorithm} algorithm\n * @param {Uint8Array} bytes\n * @returns {string}\n */\nconst applyAlgorithmToBytes = (algorithm, bytes) => {\n  return crypto.hash(algorithm, bytes, 'base64')\n}\n\n/**\n * Compares two base64 strings, allowing for base64url\n * in the second string.\n *\n * @param {string} actualValue base64 encoded string\n * @param {string} expectedValue base64 or base64url encoded string\n * @returns {boolean}\n */\nfunction caseSensitiveMatch (actualValue, expectedValue) {\n  // Ignore padding characters from the end of the strings by\n  // decreasing the length by 1 or 2 if the last characters are `=`.\n  let actualValueLength = actualValue.length\n  if (actualValueLength !== 0 && actualValue[actualValueLength - 1] === '=') {\n    actualValueLength -= 1\n  }\n  if (actualValueLength !== 0 && actualValue[actualValueLength - 1] === '=') {\n    actualValueLength -= 1\n  }\n  let expectedValueLength = expectedValue.length\n  if (expectedValueLength !== 0 && expectedValue[expectedValueLength - 1] === '=') {\n    expectedValueLength -= 1\n  }\n  if (expectedValueLength !== 0 && expectedValue[expectedValueLength - 1] === '=') {\n    expectedValueLength -= 1\n  }\n\n  if (actualValueLength !== expectedValueLength) {\n    return false\n  }\n\n  for (let i = 0; i < actualValueLength; ++i) {\n    if (\n      actualValue[i] === expectedValue[i] ||\n      (actualValue[i] === '+' && expectedValue[i] === '-') ||\n      (actualValue[i] === '/' && expectedValue[i] === '_')\n    ) {\n      continue\n    }\n    return false\n  }\n\n  return true\n}\n\nmodule.exports = {\n  applyAlgorithmToBytes,\n  bytesMatch,\n  caseSensitiveMatch,\n  isValidSRIHashAlgorithm,\n  getStrongestMetadata,\n  parseMetadata\n}\n"
  },
  {
    "path": "lib/web/webidl/index.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { types, inspect } = require('node:util')\nconst { runtimeFeatures } = require('../../util/runtime-features')\n\nconst UNDEFINED = 1\nconst BOOLEAN = 2\nconst STRING = 3\nconst SYMBOL = 4\nconst NUMBER = 5\nconst BIGINT = 6\nconst NULL = 7\nconst OBJECT = 8 // function and object\n\nconst FunctionPrototypeSymbolHasInstance = Function.call.bind(Function.prototype[Symbol.hasInstance])\n\n/** @type {import('../../../types/webidl').Webidl} */\nconst webidl = {\n  converters: {},\n  util: {},\n  errors: {},\n  is: {}\n}\n\n/**\n * @description Instantiate an error.\n *\n * @param {Object} opts\n * @param {string} opts.header\n * @param {string} opts.message\n * @returns {TypeError}\n */\nwebidl.errors.exception = function (message) {\n  return new TypeError(`${message.header}: ${message.message}`)\n}\n\n/**\n * @description Instantiate an error when conversion from one type to another has failed.\n *\n * @param {Object} opts\n * @param {string} opts.prefix\n * @param {string} opts.argument\n * @param {string[]} opts.types\n * @returns {TypeError}\n */\nwebidl.errors.conversionFailed = function (opts) {\n  const plural = opts.types.length === 1 ? '' : ' one of'\n  const message =\n    `${opts.argument} could not be converted to` +\n    `${plural}: ${opts.types.join(', ')}.`\n\n  return webidl.errors.exception({\n    header: opts.prefix,\n    message\n  })\n}\n\n/**\n * @description Instantiate an error when an invalid argument is provided\n *\n * @param {Object} context\n * @param {string} context.prefix\n * @param {string} context.value\n * @param {string} context.type\n * @returns {TypeError}\n */\nwebidl.errors.invalidArgument = function (context) {\n  return webidl.errors.exception({\n    header: context.prefix,\n    message: `\"${context.value}\" is an invalid ${context.type}.`\n  })\n}\n\n// https://webidl.spec.whatwg.org/#implements\nwebidl.brandCheck = function (V, I) {\n  if (!FunctionPrototypeSymbolHasInstance(I, V)) {\n    const err = new TypeError('Illegal invocation')\n    err.code = 'ERR_INVALID_THIS' // node compat.\n    throw err\n  }\n}\n\nwebidl.brandCheckMultiple = function (List) {\n  const prototypes = List.map((c) => webidl.util.MakeTypeAssertion(c))\n\n  return (V) => {\n    if (prototypes.every(typeCheck => !typeCheck(V))) {\n      const err = new TypeError('Illegal invocation')\n      err.code = 'ERR_INVALID_THIS' // node compat.\n      throw err\n    }\n  }\n}\n\nwebidl.argumentLengthCheck = function ({ length }, min, ctx) {\n  if (length < min) {\n    throw webidl.errors.exception({\n      message: `${min} argument${min !== 1 ? 's' : ''} required, ` +\n               `but${length ? ' only' : ''} ${length} found.`,\n      header: ctx\n    })\n  }\n}\n\nwebidl.illegalConstructor = function () {\n  throw webidl.errors.exception({\n    header: 'TypeError',\n    message: 'Illegal constructor'\n  })\n}\n\nwebidl.util.MakeTypeAssertion = function (I) {\n  return (O) => FunctionPrototypeSymbolHasInstance(I, O)\n}\n\n// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values\nwebidl.util.Type = function (V) {\n  switch (typeof V) {\n    case 'undefined': return UNDEFINED\n    case 'boolean': return BOOLEAN\n    case 'string': return STRING\n    case 'symbol': return SYMBOL\n    case 'number': return NUMBER\n    case 'bigint': return BIGINT\n    case 'function':\n    case 'object': {\n      if (V === null) {\n        return NULL\n      }\n\n      return OBJECT\n    }\n  }\n}\n\nwebidl.util.Types = {\n  UNDEFINED,\n  BOOLEAN,\n  STRING,\n  SYMBOL,\n  NUMBER,\n  BIGINT,\n  NULL,\n  OBJECT\n}\n\nwebidl.util.TypeValueToString = function (o) {\n  switch (webidl.util.Type(o)) {\n    case UNDEFINED: return 'Undefined'\n    case BOOLEAN: return 'Boolean'\n    case STRING: return 'String'\n    case SYMBOL: return 'Symbol'\n    case NUMBER: return 'Number'\n    case BIGINT: return 'BigInt'\n    case NULL: return 'Null'\n    case OBJECT: return 'Object'\n  }\n}\n\nwebidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable')\n  ? require('node:worker_threads').markAsUncloneable\n  : () => {}\n\n// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint\nwebidl.util.ConvertToInt = function (V, bitLength, signedness, flags) {\n  let upperBound\n  let lowerBound\n\n  // 1. If bitLength is 64, then:\n  if (bitLength === 64) {\n    // 1. Let upperBound be 2^53 − 1.\n    upperBound = Math.pow(2, 53) - 1\n\n    // 2. If signedness is \"unsigned\", then let lowerBound be 0.\n    if (signedness === 'unsigned') {\n      lowerBound = 0\n    } else {\n      // 3. Otherwise let lowerBound be −2^53 + 1.\n      lowerBound = Math.pow(-2, 53) + 1\n    }\n  } else if (signedness === 'unsigned') {\n    // 2. Otherwise, if signedness is \"unsigned\", then:\n\n    // 1. Let lowerBound be 0.\n    lowerBound = 0\n\n    // 2. Let upperBound be 2^bitLength − 1.\n    upperBound = Math.pow(2, bitLength) - 1\n  } else {\n    // 3. Otherwise:\n\n    // 1. Let lowerBound be -2^bitLength − 1.\n    lowerBound = Math.pow(-2, bitLength) - 1\n\n    // 2. Let upperBound be 2^bitLength − 1 − 1.\n    upperBound = Math.pow(2, bitLength - 1) - 1\n  }\n\n  // 4. Let x be ? ToNumber(V).\n  let x = Number(V)\n\n  // 5. If x is −0, then set x to +0.\n  if (x === 0) {\n    x = 0\n  }\n\n  // 6. If the conversion is to an IDL type associated\n  //    with the [EnforceRange] extended attribute, then:\n  if (webidl.util.HasFlag(flags, webidl.attributes.EnforceRange)) {\n    // 1. If x is NaN, +∞, or −∞, then throw a TypeError.\n    if (\n      Number.isNaN(x) ||\n      x === Number.POSITIVE_INFINITY ||\n      x === Number.NEGATIVE_INFINITY\n    ) {\n      throw webidl.errors.exception({\n        header: 'Integer conversion',\n        message: `Could not convert ${webidl.util.Stringify(V)} to an integer.`\n      })\n    }\n\n    // 2. Set x to IntegerPart(x).\n    x = webidl.util.IntegerPart(x)\n\n    // 3. If x < lowerBound or x > upperBound, then\n    //    throw a TypeError.\n    if (x < lowerBound || x > upperBound) {\n      throw webidl.errors.exception({\n        header: 'Integer conversion',\n        message: `Value must be between ${lowerBound}-${upperBound}, got ${x}.`\n      })\n    }\n\n    // 4. Return x.\n    return x\n  }\n\n  // 7. If x is not NaN and the conversion is to an IDL\n  //    type associated with the [Clamp] extended\n  //    attribute, then:\n  if (!Number.isNaN(x) && webidl.util.HasFlag(flags, webidl.attributes.Clamp)) {\n    // 1. Set x to min(max(x, lowerBound), upperBound).\n    x = Math.min(Math.max(x, lowerBound), upperBound)\n\n    // 2. Round x to the nearest integer, choosing the\n    //    even integer if it lies halfway between two,\n    //    and choosing +0 rather than −0.\n    if (Math.floor(x) % 2 === 0) {\n      x = Math.floor(x)\n    } else {\n      x = Math.ceil(x)\n    }\n\n    // 3. Return x.\n    return x\n  }\n\n  // 8. If x is NaN, +0, +∞, or −∞, then return +0.\n  if (\n    Number.isNaN(x) ||\n    (x === 0 && Object.is(0, x)) ||\n    x === Number.POSITIVE_INFINITY ||\n    x === Number.NEGATIVE_INFINITY\n  ) {\n    return 0\n  }\n\n  // 9. Set x to IntegerPart(x).\n  x = webidl.util.IntegerPart(x)\n\n  // 10. Set x to x modulo 2^bitLength.\n  x = x % Math.pow(2, bitLength)\n\n  // 11. If signedness is \"signed\" and x ≥ 2^bitLength − 1,\n  //    then return x − 2^bitLength.\n  if (signedness === 'signed' && x >= Math.pow(2, bitLength) - 1) {\n    return x - Math.pow(2, bitLength)\n  }\n\n  // 12. Otherwise, return x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart\nwebidl.util.IntegerPart = function (n) {\n  // 1. Let r be floor(abs(n)).\n  const r = Math.floor(Math.abs(n))\n\n  // 2. If n < 0, then return -1 × r.\n  if (n < 0) {\n    return -1 * r\n  }\n\n  // 3. Otherwise, return r.\n  return r\n}\n\nwebidl.util.Stringify = function (V) {\n  const type = webidl.util.Type(V)\n\n  switch (type) {\n    case SYMBOL:\n      return `Symbol(${V.description})`\n    case OBJECT:\n      return inspect(V)\n    case STRING:\n      return `\"${V}\"`\n    case BIGINT:\n      return `${V}n`\n    default:\n      return `${V}`\n  }\n}\n\nwebidl.util.IsResizableArrayBuffer = function (V) {\n  if (types.isArrayBuffer(V)) {\n    return V.resizable\n  }\n\n  if (types.isSharedArrayBuffer(V)) {\n    return V.growable\n  }\n\n  throw webidl.errors.exception({\n    header: 'IsResizableArrayBuffer',\n    message: `\"${webidl.util.Stringify(V)}\" is not an array buffer.`\n  })\n}\n\nwebidl.util.HasFlag = function (flags, attributes) {\n  return typeof flags === 'number' && (flags & attributes) === attributes\n}\n\n// https://webidl.spec.whatwg.org/#es-sequence\nwebidl.sequenceConverter = function (converter) {\n  return (V, prefix, argument, Iterable) => {\n    // 1. If Type(V) is not Object, throw a TypeError.\n    if (webidl.util.Type(V) !== OBJECT) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: `${argument} (${webidl.util.Stringify(V)}) is not iterable.`\n      })\n    }\n\n    // 2. Let method be ? GetMethod(V, @@iterator).\n    /** @type {Generator} */\n    const method = typeof Iterable === 'function' ? Iterable() : V?.[Symbol.iterator]?.()\n    const seq = []\n    let index = 0\n\n    // 3. If method is undefined, throw a TypeError.\n    if (\n      method === undefined ||\n      typeof method.next !== 'function'\n    ) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: `${argument} is not iterable.`\n      })\n    }\n\n    // https://webidl.spec.whatwg.org/#create-sequence-from-iterable\n    while (true) {\n      const { done, value } = method.next()\n\n      if (done) {\n        break\n      }\n\n      seq.push(converter(value, prefix, `${argument}[${index++}]`))\n    }\n\n    return seq\n  }\n}\n\n// https://webidl.spec.whatwg.org/#es-to-record\nwebidl.recordConverter = function (keyConverter, valueConverter) {\n  return (O, prefix, argument) => {\n    // 1. If Type(O) is not Object, throw a TypeError.\n    if (webidl.util.Type(O) !== OBJECT) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: `${argument} (\"${webidl.util.TypeValueToString(O)}\") is not an Object.`\n      })\n    }\n\n    // 2. Let result be a new empty instance of record<K, V>.\n    const result = {}\n\n    if (!types.isProxy(O)) {\n      // 1. Let desc be ? O.[[GetOwnProperty]](key).\n      const keys = [...Object.getOwnPropertyNames(O), ...Object.getOwnPropertySymbols(O)]\n\n      for (const key of keys) {\n        const keyName = webidl.util.Stringify(key)\n\n        // 1. Let typedKey be key converted to an IDL value of type K.\n        const typedKey = keyConverter(key, prefix, `Key ${keyName} in ${argument}`)\n\n        // 2. Let value be ? Get(O, key).\n        // 3. Let typedValue be value converted to an IDL value of type V.\n        const typedValue = valueConverter(O[key], prefix, `${argument}[${keyName}]`)\n\n        // 4. Set result[typedKey] to typedValue.\n        result[typedKey] = typedValue\n      }\n\n      // 5. Return result.\n      return result\n    }\n\n    // 3. Let keys be ? O.[[OwnPropertyKeys]]().\n    const keys = Reflect.ownKeys(O)\n\n    // 4. For each key of keys.\n    for (const key of keys) {\n      // 1. Let desc be ? O.[[GetOwnProperty]](key).\n      const desc = Reflect.getOwnPropertyDescriptor(O, key)\n\n      // 2. If desc is not undefined and desc.[[Enumerable]] is true:\n      if (desc?.enumerable) {\n        // 1. Let typedKey be key converted to an IDL value of type K.\n        const typedKey = keyConverter(key, prefix, argument)\n\n        // 2. Let value be ? Get(O, key).\n        // 3. Let typedValue be value converted to an IDL value of type V.\n        const typedValue = valueConverter(O[key], prefix, argument)\n\n        // 4. Set result[typedKey] to typedValue.\n        result[typedKey] = typedValue\n      }\n    }\n\n    // 5. Return result.\n    return result\n  }\n}\n\nwebidl.interfaceConverter = function (TypeCheck, name) {\n  return (V, prefix, argument) => {\n    if (!TypeCheck(V)) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: `Expected ${argument} (\"${webidl.util.Stringify(V)}\") to be an instance of ${name}.`\n      })\n    }\n\n    return V\n  }\n}\n\nwebidl.dictionaryConverter = function (converters) {\n  // \"For each dictionary member member declared on dictionary, in lexicographical order:\"\n  converters.sort((a, b) => (a.key > b.key) - (a.key < b.key))\n\n  return (dictionary, prefix, argument) => {\n    const dict = {}\n\n    if (dictionary != null && webidl.util.Type(dictionary) !== OBJECT) {\n      throw webidl.errors.exception({\n        header: prefix,\n        message: `Expected ${dictionary} to be one of: Null, Undefined, Object.`\n      })\n    }\n\n    for (const options of converters) {\n      const { key, defaultValue, required, converter } = options\n\n      if (required === true) {\n        if (dictionary == null || !Object.hasOwn(dictionary, key)) {\n          throw webidl.errors.exception({\n            header: prefix,\n            message: `Missing required key \"${key}\".`\n          })\n        }\n      }\n\n      let value = dictionary?.[key]\n      const hasDefault = defaultValue !== undefined\n\n      // Only use defaultValue if value is undefined and\n      // a defaultValue options was provided.\n      if (hasDefault && value === undefined) {\n        value = defaultValue()\n      }\n\n      // A key can be optional and have no default value.\n      // When this happens, do not perform a conversion,\n      // and do not assign the key a value.\n      if (required || hasDefault || value !== undefined) {\n        value = converter(value, prefix, `${argument}.${key}`)\n\n        if (\n          options.allowedValues &&\n          !options.allowedValues.includes(value)\n        ) {\n          throw webidl.errors.exception({\n            header: prefix,\n            message: `${value} is not an accepted type. Expected one of ${options.allowedValues.join(', ')}.`\n          })\n        }\n\n        dict[key] = value\n      }\n    }\n\n    return dict\n  }\n}\n\nwebidl.nullableConverter = function (converter) {\n  return (V, prefix, argument) => {\n    if (V === null) {\n      return V\n    }\n\n    return converter(V, prefix, argument)\n  }\n}\n\n/**\n * @param {*} value\n * @returns {boolean}\n */\nwebidl.is.USVString = function (value) {\n  return (\n    typeof value === 'string' &&\n    value.isWellFormed()\n  )\n}\n\nwebidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream)\nwebidl.is.Blob = webidl.util.MakeTypeAssertion(Blob)\nwebidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams)\nwebidl.is.File = webidl.util.MakeTypeAssertion(File)\nwebidl.is.URL = webidl.util.MakeTypeAssertion(URL)\nwebidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal)\nwebidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort)\n\nwebidl.is.BufferSource = function (V) {\n  return types.isArrayBuffer(V) || (\n    ArrayBuffer.isView(V) &&\n    types.isArrayBuffer(V.buffer)\n  )\n}\n\n// https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy\nwebidl.util.getCopyOfBytesHeldByBufferSource = function (bufferSource) {\n  // 1. Let jsBufferSource be the result of converting bufferSource to a JavaScript value.\n  const jsBufferSource = bufferSource\n\n  // 2. Let jsArrayBuffer be jsBufferSource.\n  let jsArrayBuffer = jsBufferSource\n\n  // 3. Let offset be 0.\n  let offset = 0\n\n  // 4. Let length be 0.\n  let length = 0\n\n  // 5. If jsBufferSource has a [[ViewedArrayBuffer]] internal slot, then:\n  if (types.isTypedArray(jsBufferSource) || types.isDataView(jsBufferSource)) {\n    // 5.1. Set jsArrayBuffer to jsBufferSource.[[ViewedArrayBuffer]].\n    jsArrayBuffer = jsBufferSource.buffer\n\n    // 5.2. Set offset to jsBufferSource.[[ByteOffset]].\n    offset = jsBufferSource.byteOffset\n\n    // 5.3. Set length to jsBufferSource.[[ByteLength]].\n    length = jsBufferSource.byteLength\n  } else {\n    // 6. Otherwise:\n\n    // 6.1. Assert: jsBufferSource is an ArrayBuffer or SharedArrayBuffer object.\n    assert(types.isAnyArrayBuffer(jsBufferSource))\n\n    // 6.2. Set length to jsBufferSource.[[ArrayBufferByteLength]].\n    length = jsBufferSource.byteLength\n  }\n\n  // 7. If IsDetachedBuffer(jsArrayBuffer) is true, then return the empty byte sequence.\n  if (jsArrayBuffer.detached) {\n    return new Uint8Array(0)\n  }\n\n  // 8. Let bytes be a new byte sequence of length equal to length.\n  const bytes = new Uint8Array(length)\n\n  // 9. For i in the range offset to offset + length − 1, inclusive,\n  //    set bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true, Unordered).\n  const view = new Uint8Array(jsArrayBuffer, offset, length)\n  bytes.set(view)\n\n  // 10. Return bytes.\n  return bytes\n}\n\n// https://webidl.spec.whatwg.org/#es-DOMString\nwebidl.converters.DOMString = function (V, prefix, argument, flags) {\n  // 1. If V is null and the conversion is to an IDL type\n  //    associated with the [LegacyNullToEmptyString]\n  //    extended attribute, then return the DOMString value\n  //    that represents the empty string.\n  if (V === null && webidl.util.HasFlag(flags, webidl.attributes.LegacyNullToEmptyString)) {\n    return ''\n  }\n\n  // 2. Let x be ? ToString(V).\n  if (typeof V === 'symbol') {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} is a symbol, which cannot be converted to a DOMString.`\n    })\n  }\n\n  // 3. Return the IDL DOMString value that represents the\n  //    same sequence of code units as the one the\n  //    ECMAScript String value x represents.\n  return String(V)\n}\n\n// https://webidl.spec.whatwg.org/#es-ByteString\nwebidl.converters.ByteString = function (V, prefix, argument) {\n  // 1. Let x be ? ToString(V).\n  if (typeof V === 'symbol') {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} is a symbol, which cannot be converted to a ByteString.`\n    })\n  }\n\n  const x = String(V)\n\n  // 2. If the value of any element of x is greater than\n  //    255, then throw a TypeError.\n  for (let index = 0; index < x.length; index++) {\n    if (x.charCodeAt(index) > 255) {\n      throw new TypeError(\n        'Cannot convert argument to a ByteString because the character at ' +\n        `index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`\n      )\n    }\n  }\n\n  // 3. Return an IDL ByteString value whose length is the\n  //    length of x, and where the value of each element is\n  //    the value of the corresponding element of x.\n  return x\n}\n\n/**\n * @param {unknown} value\n * @returns {string}\n * @see https://webidl.spec.whatwg.org/#es-USVString\n */\nwebidl.converters.USVString = function (value) {\n  // TODO: rewrite this so we can control the errors thrown\n  if (typeof value === 'string') {\n    return value.toWellFormed()\n  }\n  return `${value}`.toWellFormed()\n}\n\n// https://webidl.spec.whatwg.org/#es-boolean\nwebidl.converters.boolean = function (V) {\n  // 1. Let x be the result of computing ToBoolean(V).\n  // https://262.ecma-international.org/10.0/index.html#table-10\n  const x = Boolean(V)\n\n  // 2. Return the IDL boolean value that is the one that represents\n  //    the same truth value as the ECMAScript Boolean value x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#es-any\nwebidl.converters.any = function (V) {\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#es-long-long\nwebidl.converters['long long'] = function (V, prefix, argument) {\n  // 1. Let x be ? ConvertToInt(V, 64, \"signed\").\n  const x = webidl.util.ConvertToInt(V, 64, 'signed', 0, prefix, argument)\n\n  // 2. Return the IDL long long value that represents\n  //    the same numeric value as x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#es-unsigned-long-long\nwebidl.converters['unsigned long long'] = function (V, prefix, argument) {\n  // 1. Let x be ? ConvertToInt(V, 64, \"unsigned\").\n  const x = webidl.util.ConvertToInt(V, 64, 'unsigned', 0, prefix, argument)\n\n  // 2. Return the IDL unsigned long long value that\n  //    represents the same numeric value as x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#es-unsigned-long\nwebidl.converters['unsigned long'] = function (V, prefix, argument) {\n  // 1. Let x be ? ConvertToInt(V, 32, \"unsigned\").\n  const x = webidl.util.ConvertToInt(V, 32, 'unsigned', 0, prefix, argument)\n\n  // 2. Return the IDL unsigned long value that\n  //    represents the same numeric value as x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#es-unsigned-short\nwebidl.converters['unsigned short'] = function (V, prefix, argument, flags) {\n  // 1. Let x be ? ConvertToInt(V, 16, \"unsigned\").\n  const x = webidl.util.ConvertToInt(V, 16, 'unsigned', flags, prefix, argument)\n\n  // 2. Return the IDL unsigned short value that represents\n  //    the same numeric value as x.\n  return x\n}\n\n// https://webidl.spec.whatwg.org/#idl-ArrayBuffer\nwebidl.converters.ArrayBuffer = function (V, prefix, argument, flags) {\n  // 1. If V is not an Object, or V does not have an\n  //    [[ArrayBufferData]] internal slot, then throw a\n  //    TypeError.\n  // 2. If IsSharedArrayBuffer(V) is true, then throw a\n  //    TypeError.\n  // see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances\n  if (\n    webidl.util.Type(V) !== OBJECT ||\n    !types.isArrayBuffer(V)\n  ) {\n    throw webidl.errors.conversionFailed({\n      prefix,\n      argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n      types: ['ArrayBuffer']\n    })\n  }\n\n  // 3. If the conversion is not to an IDL type associated\n  //    with the [AllowResizable] extended attribute, and\n  //    IsResizableArrayBuffer(V) is true, then throw a\n  //    TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a resizable ArrayBuffer.`\n    })\n  }\n\n  // 4. Return the IDL ArrayBuffer value that is a\n  //    reference to the same object as V.\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#idl-SharedArrayBuffer\nwebidl.converters.SharedArrayBuffer = function (V, prefix, argument, flags) {\n  // 1. If V is not an Object, or V does not have an\n  //    [[ArrayBufferData]] internal slot, then throw a\n  //    TypeError.\n  // 2. If IsSharedArrayBuffer(V) is false, then throw a\n  //    TypeError.\n  // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances\n  if (\n    webidl.util.Type(V) !== OBJECT ||\n    !types.isSharedArrayBuffer(V)\n  ) {\n    throw webidl.errors.conversionFailed({\n      prefix,\n      argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n      types: ['SharedArrayBuffer']\n    })\n  }\n\n  // 3. If the conversion is not to an IDL type associated\n  //    with the [AllowResizable] extended attribute, and\n  //    IsResizableArrayBuffer(V) is true, then throw a\n  //    TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a resizable SharedArrayBuffer.`\n    })\n  }\n\n  // 4. Return the IDL SharedArrayBuffer value that is a\n  //    reference to the same object as V.\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#dfn-typed-array-type\nwebidl.converters.TypedArray = function (V, T, prefix, argument, flags) {\n  // 1. Let T be the IDL type V is being converted to.\n\n  // 2. If Type(V) is not Object, or V does not have a\n  //    [[TypedArrayName]] internal slot with a value\n  //    equal to T’s name, then throw a TypeError.\n  if (\n    webidl.util.Type(V) !== OBJECT ||\n    !types.isTypedArray(V) ||\n    V.constructor.name !== T.name\n  ) {\n    throw webidl.errors.conversionFailed({\n      prefix,\n      argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n      types: [T.name]\n    })\n  }\n\n  // 3. If the conversion is not to an IDL type associated\n  //    with the [AllowShared] extended attribute, and\n  //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is\n  //    true, then throw a TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a shared array buffer.`\n    })\n  }\n\n  // 4. If the conversion is not to an IDL type associated\n  //    with the [AllowResizable] extended attribute, and\n  //    IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is\n  //    true, then throw a TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a resizable array buffer.`\n    })\n  }\n\n  // 5. Return the IDL value of type T that is a reference\n  //    to the same object as V.\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#idl-DataView\nwebidl.converters.DataView = function (V, prefix, argument, flags) {\n  // 1. If Type(V) is not Object, or V does not have a\n  //    [[DataView]] internal slot, then throw a TypeError.\n  if (webidl.util.Type(V) !== OBJECT || !types.isDataView(V)) {\n    throw webidl.errors.conversionFailed({\n      prefix,\n      argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n      types: ['DataView']\n    })\n  }\n\n  // 2. If the conversion is not to an IDL type associated\n  //    with the [AllowShared] extended attribute, and\n  //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true,\n  //    then throw a TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a shared array buffer.`\n    })\n  }\n\n  // 3. If the conversion is not to an IDL type associated\n  //    with the [AllowResizable] extended attribute, and\n  //    IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is\n  //    true, then throw a TypeError.\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a resizable array buffer.`\n    })\n  }\n\n  // 4. Return the IDL DataView value that is a reference\n  //    to the same object as V.\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#ArrayBufferView\nwebidl.converters.ArrayBufferView = function (V, prefix, argument, flags) {\n  if (\n    webidl.util.Type(V) !== OBJECT ||\n    !types.isArrayBufferView(V)\n  ) {\n    throw webidl.errors.conversionFailed({\n      prefix,\n      argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n      types: ['ArrayBufferView']\n    })\n  }\n\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowShared) && types.isSharedArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a shared array buffer.`\n    })\n  }\n\n  if (!webidl.util.HasFlag(flags, webidl.attributes.AllowResizable) && webidl.util.IsResizableArrayBuffer(V.buffer)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a view on a resizable array buffer.`\n    })\n  }\n\n  return V\n}\n\n// https://webidl.spec.whatwg.org/#BufferSource\nwebidl.converters.BufferSource = function (V, prefix, argument, flags) {\n  if (types.isArrayBuffer(V)) {\n    return webidl.converters.ArrayBuffer(V, prefix, argument, flags)\n  }\n\n  if (types.isArrayBufferView(V)) {\n    flags &= ~webidl.attributes.AllowShared\n\n    return webidl.converters.ArrayBufferView(V, prefix, argument, flags)\n  }\n\n  // Make this explicit for easier debugging\n  if (types.isSharedArrayBuffer(V)) {\n    throw webidl.errors.exception({\n      header: prefix,\n      message: `${argument} cannot be a SharedArrayBuffer.`\n    })\n  }\n\n  throw webidl.errors.conversionFailed({\n    prefix,\n    argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n    types: ['ArrayBuffer', 'ArrayBufferView']\n  })\n}\n\n// https://webidl.spec.whatwg.org/#AllowSharedBufferSource\nwebidl.converters.AllowSharedBufferSource = function (V, prefix, argument, flags) {\n  if (types.isArrayBuffer(V)) {\n    return webidl.converters.ArrayBuffer(V, prefix, argument, flags)\n  }\n\n  if (types.isSharedArrayBuffer(V)) {\n    return webidl.converters.SharedArrayBuffer(V, prefix, argument, flags)\n  }\n\n  if (types.isArrayBufferView(V)) {\n    flags |= webidl.attributes.AllowShared\n    return webidl.converters.ArrayBufferView(V, prefix, argument, flags)\n  }\n\n  throw webidl.errors.conversionFailed({\n    prefix,\n    argument: `${argument} (\"${webidl.util.Stringify(V)}\")`,\n    types: ['ArrayBuffer', 'SharedArrayBuffer', 'ArrayBufferView']\n  })\n}\n\nwebidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(\n  webidl.converters.ByteString\n)\n\nwebidl.converters['sequence<sequence<ByteString>>'] = webidl.sequenceConverter(\n  webidl.converters['sequence<ByteString>']\n)\n\nwebidl.converters['record<ByteString, ByteString>'] = webidl.recordConverter(\n  webidl.converters.ByteString,\n  webidl.converters.ByteString\n)\n\nwebidl.converters.Blob = webidl.interfaceConverter(webidl.is.Blob, 'Blob')\n\nwebidl.converters.AbortSignal = webidl.interfaceConverter(\n  webidl.is.AbortSignal,\n  'AbortSignal'\n)\n\n/**\n * [LegacyTreatNonObjectAsNull]\n * callback EventHandlerNonNull = any (Event event);\n * typedef EventHandlerNonNull? EventHandler;\n * @param {*} V\n */\nwebidl.converters.EventHandlerNonNull = function (V) {\n  if (webidl.util.Type(V) !== OBJECT) {\n    return null\n  }\n\n  // [I]f the value is not an object, it will be converted to null, and if the value is not callable,\n  // it will be converted to a callback function value that does nothing when called.\n  if (typeof V === 'function') {\n    return V\n  }\n\n  return () => {}\n}\n\nwebidl.attributes = {\n  Clamp: 1 << 0,\n  EnforceRange: 1 << 1,\n  AllowShared: 1 << 2,\n  AllowResizable: 1 << 3,\n  LegacyNullToEmptyString: 1 << 4\n}\n\nmodule.exports = {\n  webidl\n}\n"
  },
  {
    "path": "lib/web/websocket/connection.js",
    "content": "'use strict'\n\nconst { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')\nconst { parseExtensions, isClosed, isClosing, isEstablished, isConnecting, validateCloseCodeAndReason } = require('./util')\nconst { makeRequest } = require('../fetch/request')\nconst { fetching } = require('../fetch/index')\nconst { Headers, getHeadersList } = require('../fetch/headers')\nconst { getDecodeSplit } = require('../fetch/util')\nconst { WebsocketFrameSend } = require('./frame')\nconst assert = require('node:assert')\nconst { runtimeFeatures } = require('../../util/runtime-features')\n\nconst crypto = runtimeFeatures.has('crypto')\n  ? require('node:crypto')\n  : null\n\nlet warningEmitted = false\n\n/**\n * @see https://websockets.spec.whatwg.org/#concept-websocket-establish\n * @param {URL} url\n * @param {string|string[]} protocols\n * @param {import('./websocket').Handler} handler\n * @param {Partial<import('../../../types/websocket').WebSocketInit>} options\n */\nfunction establishWebSocketConnection (url, protocols, client, handler, options) {\n  // 1. Let requestURL be a copy of url, with its scheme set to \"http\", if url’s\n  //    scheme is \"ws\", and to \"https\" otherwise.\n  const requestURL = url\n\n  requestURL.protocol = url.protocol === 'ws:' ? 'http:' : 'https:'\n\n  // 2. Let request be a new request, whose URL is requestURL, client is client,\n  //    service-workers mode is \"none\", referrer is \"no-referrer\", mode is\n  //    \"websocket\", credentials mode is \"include\", cache mode is \"no-store\" ,\n  //    redirect mode is \"error\", and use-URL-credentials flag is set.\n  const request = makeRequest({\n    urlList: [requestURL],\n    client,\n    serviceWorkers: 'none',\n    referrer: 'no-referrer',\n    mode: 'websocket',\n    credentials: 'include',\n    cache: 'no-store',\n    redirect: 'error',\n    useURLCredentials: true\n  })\n\n  // Note: undici extension, allow setting custom headers.\n  if (options.headers) {\n    const headersList = getHeadersList(new Headers(options.headers))\n\n    request.headersList = headersList\n  }\n\n  // 3. Append (`Upgrade`, `websocket`) to request’s header list.\n  // 4. Append (`Connection`, `Upgrade`) to request’s header list.\n  // Note: both of these are handled by undici currently.\n  // https://github.com/nodejs/undici/blob/68c269c4144c446f3f1220951338daef4a6b5ec4/lib/client.js#L1397\n\n  // 5. Let keyValue be a nonce consisting of a randomly selected\n  //    16-byte value that has been forgiving-base64-encoded and\n  //    isomorphic encoded.\n  const keyValue = crypto.randomBytes(16).toString('base64')\n\n  // 6. Append (`Sec-WebSocket-Key`, keyValue) to request’s\n  //    header list.\n  request.headersList.append('sec-websocket-key', keyValue, true)\n\n  // 7. Append (`Sec-WebSocket-Version`, `13`) to request’s\n  //    header list.\n  request.headersList.append('sec-websocket-version', '13', true)\n\n  // 8. For each protocol in protocols, combine\n  //    (`Sec-WebSocket-Protocol`, protocol) in request’s header\n  //    list.\n  for (const protocol of protocols) {\n    request.headersList.append('sec-websocket-protocol', protocol, true)\n  }\n\n  // 9. Let permessageDeflate be a user-agent defined\n  //    \"permessage-deflate\" extension header value.\n  // https://github.com/mozilla/gecko-dev/blob/ce78234f5e653a5d3916813ff990f053510227bc/netwerk/protocol/websocket/WebSocketChannel.cpp#L2673\n  const permessageDeflate = 'permessage-deflate; client_max_window_bits'\n\n  // 10. Append (`Sec-WebSocket-Extensions`, permessageDeflate) to\n  //     request’s header list.\n  request.headersList.append('sec-websocket-extensions', permessageDeflate, true)\n\n  // 11. Fetch request with useParallelQueue set to true, and\n  //     processResponse given response being these steps:\n  const controller = fetching({\n    request,\n    useParallelQueue: true,\n    dispatcher: options.dispatcher,\n    processResponse (response) {\n      // 1. If response is a network error or its status is not 101,\n      //    fail the WebSocket connection.\n      // if (response.type === 'error' || ((response.socket?.session != null && response.status !== 200) && response.status !== 101)) {\n      if (response.type === 'error' || response.status !== 101) {\n        // The presence of a session property on the socket indicates HTTP2\n        // HTTP1\n        if (response.socket?.session == null) {\n          failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)\n          return\n        }\n\n        // HTTP2\n        if (response.status !== 200) {\n          failWebsocketConnection(handler, 1002, 'Received network error or non-200 status code.', response.error)\n          return\n        }\n      }\n\n      if (warningEmitted === false && response.socket?.session != null) {\n        process.emitWarning('WebSocket over HTTP2 is experimental, and subject to change.', 'ExperimentalWarning')\n        warningEmitted = true\n      }\n\n      // 2. If protocols is not the empty list and extracting header\n      //    list values given `Sec-WebSocket-Protocol` and response’s\n      //    header list results in null, failure, or the empty byte\n      //    sequence, then fail the WebSocket connection.\n      if (protocols.length !== 0 && !response.headersList.get('Sec-WebSocket-Protocol')) {\n        failWebsocketConnection(handler, 1002, 'Server did not respond with sent protocols.')\n        return\n      }\n\n      // 3. Follow the requirements stated step 2 to step 6, inclusive,\n      //    of the last set of steps in section 4.1 of The WebSocket\n      //    Protocol to validate response. This either results in fail\n      //    the WebSocket connection or the WebSocket connection is\n      //    established.\n\n      // 2. If the response lacks an |Upgrade| header field or the |Upgrade|\n      //    header field contains a value that is not an ASCII case-\n      //    insensitive match for the value \"websocket\", the client MUST\n      //    _Fail the WebSocket Connection_.\n      //    For H2, no upgrade header is expected.\n      if (response.socket.session == null && response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {\n        failWebsocketConnection(handler, 1002, 'Server did not set Upgrade header to \"websocket\".')\n        return\n      }\n\n      // 3. If the response lacks a |Connection| header field or the\n      //    |Connection| header field doesn't contain a token that is an\n      //    ASCII case-insensitive match for the value \"Upgrade\", the client\n      //    MUST _Fail the WebSocket Connection_.\n      //    For H2, no connection header is expected.\n      if (response.socket.session == null && response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {\n        failWebsocketConnection(handler, 1002, 'Server did not set Connection header to \"upgrade\".')\n        return\n      }\n\n      // 4. If the response lacks a |Sec-WebSocket-Accept| header field or\n      //    the |Sec-WebSocket-Accept| contains a value other than the\n      //    base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket-\n      //    Key| (as a string, not base64-decoded) with the string \"258EAFA5-\n      //    E914-47DA-95CA-C5AB0DC85B11\" but ignoring any leading and\n      //    trailing whitespace, the client MUST _Fail the WebSocket\n      //    Connection_.\n      const secWSAccept = response.headersList.get('Sec-WebSocket-Accept')\n      const digest = crypto.hash('sha1', keyValue + uid, 'base64')\n      if (secWSAccept !== digest) {\n        failWebsocketConnection(handler, 1002, 'Incorrect hash received in Sec-WebSocket-Accept header.')\n        return\n      }\n\n      // 5. If the response includes a |Sec-WebSocket-Extensions| header\n      //    field and this header field indicates the use of an extension\n      //    that was not present in the client's handshake (the server has\n      //    indicated an extension not requested by the client), the client\n      //    MUST _Fail the WebSocket Connection_.  (The parsing of this\n      //    header field to determine which extensions are requested is\n      //    discussed in Section 9.1.)\n      const secExtension = response.headersList.get('Sec-WebSocket-Extensions')\n      let extensions\n\n      if (secExtension !== null) {\n        extensions = parseExtensions(secExtension)\n\n        if (!extensions.has('permessage-deflate')) {\n          failWebsocketConnection(handler, 1002, 'Sec-WebSocket-Extensions header does not match.')\n          return\n        }\n      }\n\n      // 6. If the response includes a |Sec-WebSocket-Protocol| header field\n      //    and this header field indicates the use of a subprotocol that was\n      //    not present in the client's handshake (the server has indicated a\n      //    subprotocol not requested by the client), the client MUST _Fail\n      //    the WebSocket Connection_.\n      const secProtocol = response.headersList.get('Sec-WebSocket-Protocol')\n\n      if (secProtocol !== null) {\n        const requestProtocols = getDecodeSplit('sec-websocket-protocol', request.headersList)\n\n        // The client can request that the server use a specific subprotocol by\n        // including the |Sec-WebSocket-Protocol| field in its handshake.  If it\n        // is specified, the server needs to include the same field and one of\n        // the selected subprotocol values in its response for the connection to\n        // be established.\n        if (!requestProtocols.includes(secProtocol)) {\n          failWebsocketConnection(handler, 1002, 'Protocol was not set in the opening handshake.')\n          return\n        }\n      }\n\n      response.socket.on('data', handler.onSocketData)\n      response.socket.on('close', handler.onSocketClose)\n      response.socket.on('error', handler.onSocketError)\n\n      handler.wasEverConnected = true\n      handler.onConnectionEstablished(response, extensions)\n    }\n  })\n\n  return controller\n}\n\n/**\n * @see https://whatpr.org/websockets/48.html#close-the-websocket\n * @param {import('./websocket').Handler} object\n * @param {number} [code=null]\n * @param {string} [reason='']\n */\nfunction closeWebSocketConnection (object, code, reason, validate = false) {\n  // 1. If code was not supplied, let code be null.\n  code ??= null\n\n  // 2. If reason was not supplied, let reason be the empty string.\n  reason ??= ''\n\n  // 3. Validate close code and reason with code and reason.\n  if (validate) validateCloseCodeAndReason(code, reason)\n\n  // 4. Run the first matching steps from the following list:\n  //     - If object’s ready state is CLOSING (2) or CLOSED (3)\n  //     - If the WebSocket connection is not yet established [WSP]\n  //     - If the WebSocket closing handshake has not yet been started [WSP]\n  //     - Otherwise\n  if (isClosed(object.readyState) || isClosing(object.readyState)) {\n    // Do nothing.\n  } else if (!isEstablished(object.readyState)) {\n    // Fail the WebSocket connection and set object’s ready state to CLOSING (2). [WSP]\n    failWebsocketConnection(object)\n    object.readyState = states.CLOSING\n  } else if (!object.closeState.has(sentCloseFrameState.SENT) && !object.closeState.has(sentCloseFrameState.RECEIVED)) {\n    // Upon either sending or receiving a Close control frame, it is said\n    // that _The WebSocket Closing Handshake is Started_ and that the\n    // WebSocket connection is in the CLOSING state.\n\n    const frame = new WebsocketFrameSend()\n\n    // If neither code nor reason is present, the WebSocket Close\n    // message must not have a body.\n\n    // If code is present, then the status code to use in the\n    // WebSocket Close message must be the integer given by code.\n    // If code is null and reason is the empty string, the WebSocket Close frame must not have a body.\n    // If reason is non-empty but code is null, then set code to 1000 (\"Normal Closure\").\n    if (reason.length !== 0 && code === null) {\n      code = 1000\n    }\n\n    // If code is set, then the status code to use in the WebSocket Close frame must be the integer given by code.\n    assert(code === null || Number.isInteger(code))\n\n    if (code === null && reason.length === 0) {\n      frame.frameData = emptyBuffer\n    } else if (code !== null && reason === null) {\n      frame.frameData = Buffer.allocUnsafe(2)\n      frame.frameData.writeUInt16BE(code, 0)\n    } else if (code !== null && reason !== null) {\n      // If reason is also present, then reasonBytes must be\n      // provided in the Close message after the status code.\n      frame.frameData = Buffer.allocUnsafe(2 + Buffer.byteLength(reason))\n      frame.frameData.writeUInt16BE(code, 0)\n      // the body MAY contain UTF-8-encoded data with value /reason/\n      frame.frameData.write(reason, 2, 'utf-8')\n    } else {\n      frame.frameData = emptyBuffer\n    }\n\n    object.socket.write(frame.createFrame(opcodes.CLOSE))\n\n    object.closeState.add(sentCloseFrameState.SENT)\n\n    // Upon either sending or receiving a Close control frame, it is said\n    // that _The WebSocket Closing Handshake is Started_ and that the\n    // WebSocket connection is in the CLOSING state.\n    object.readyState = states.CLOSING\n  } else {\n    // Set object’s ready state to CLOSING (2).\n    object.readyState = states.CLOSING\n  }\n}\n\n/**\n * @param {import('./websocket').Handler} handler\n * @param {number} code\n * @param {string|undefined} reason\n * @param {unknown} cause\n * @returns {void}\n */\nfunction failWebsocketConnection (handler, code, reason, cause) {\n  // If _The WebSocket Connection is Established_ prior to the point where\n  // the endpoint is required to _Fail the WebSocket Connection_, the\n  // endpoint SHOULD send a Close frame with an appropriate status code\n  // (Section 7.4) before proceeding to _Close the WebSocket Connection_.\n  if (isEstablished(handler.readyState)) {\n    closeWebSocketConnection(handler, code, reason, false)\n  }\n\n  handler.controller.abort()\n\n  if (isConnecting(handler.readyState)) {\n    // If the connection was not established, we must still emit an 'error' and 'close' events\n    handler.onSocketClose()\n  } else if (handler.socket?.destroyed === false) {\n    handler.socket.destroy()\n  }\n}\n\nmodule.exports = {\n  establishWebSocketConnection,\n  failWebsocketConnection,\n  closeWebSocketConnection\n}\n"
  },
  {
    "path": "lib/web/websocket/constants.js",
    "content": "'use strict'\n\n/**\n * This is a Globally Unique Identifier unique used to validate that the\n * endpoint accepts websocket connections.\n * @see https://www.rfc-editor.org/rfc/rfc6455.html#section-1.3\n * @type {'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'}\n */\nconst uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n\n/**\n * @type {PropertyDescriptor}\n */\nconst staticPropertyDescriptors = {\n  enumerable: true,\n  writable: false,\n  configurable: false\n}\n\n/**\n * The states of the WebSocket connection.\n *\n * @readonly\n * @enum\n * @property {0} CONNECTING\n * @property {1} OPEN\n * @property {2} CLOSING\n * @property {3} CLOSED\n */\nconst states = {\n  CONNECTING: 0,\n  OPEN: 1,\n  CLOSING: 2,\n  CLOSED: 3\n}\n\n/**\n * @readonly\n * @enum\n * @property {0} NOT_SENT\n * @property {1} PROCESSING\n * @property {2} SENT\n */\nconst sentCloseFrameState = {\n  SENT: 1,\n  RECEIVED: 2\n}\n\n/**\n * The WebSocket opcodes.\n *\n * @readonly\n * @enum\n * @property {0x0} CONTINUATION\n * @property {0x1} TEXT\n * @property {0x2} BINARY\n * @property {0x8} CLOSE\n * @property {0x9} PING\n * @property {0xA} PONG\n * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2\n */\nconst opcodes = {\n  CONTINUATION: 0x0,\n  TEXT: 0x1,\n  BINARY: 0x2,\n  CLOSE: 0x8,\n  PING: 0x9,\n  PONG: 0xA\n}\n\n/**\n * The maximum value for an unsigned 16-bit integer.\n *\n * @type {65535} 2 ** 16 - 1\n */\nconst maxUnsigned16Bit = 65535\n\n/**\n * The states of the parser.\n *\n * @readonly\n * @enum\n * @property {0} INFO\n * @property {2} PAYLOADLENGTH_16\n * @property {3} PAYLOADLENGTH_64\n * @property {4} READ_DATA\n */\nconst parserStates = {\n  INFO: 0,\n  PAYLOADLENGTH_16: 2,\n  PAYLOADLENGTH_64: 3,\n  READ_DATA: 4\n}\n\n/**\n * An empty buffer.\n *\n * @type {Buffer}\n */\nconst emptyBuffer = Buffer.allocUnsafe(0)\n\n/**\n * @readonly\n * @property {1} text\n * @property {2} typedArray\n * @property {3} arrayBuffer\n * @property {4} blob\n */\nconst sendHints = {\n  text: 1,\n  typedArray: 2,\n  arrayBuffer: 3,\n  blob: 4\n}\n\nmodule.exports = {\n  uid,\n  sentCloseFrameState,\n  staticPropertyDescriptors,\n  states,\n  opcodes,\n  maxUnsigned16Bit,\n  parserStates,\n  emptyBuffer,\n  sendHints\n}\n"
  },
  {
    "path": "lib/web/websocket/events.js",
    "content": "'use strict'\n\nconst { webidl } = require('../webidl')\nconst { kEnumerableProperty } = require('../../core/util')\nconst { kConstruct } = require('../../core/symbols')\n\n/**\n * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent\n */\nclass MessageEvent extends Event {\n  #eventInit\n\n  constructor (type, eventInitDict = {}) {\n    if (type === kConstruct) {\n      super(arguments[1], arguments[2])\n      webidl.util.markAsUncloneable(this)\n      return\n    }\n\n    const prefix = 'MessageEvent constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    type = webidl.converters.DOMString(type, prefix, 'type')\n    eventInitDict = webidl.converters.MessageEventInit(eventInitDict, prefix, 'eventInitDict')\n\n    super(type, eventInitDict)\n\n    this.#eventInit = eventInitDict\n    webidl.util.markAsUncloneable(this)\n  }\n\n  get data () {\n    webidl.brandCheck(this, MessageEvent)\n\n    return this.#eventInit.data\n  }\n\n  get origin () {\n    webidl.brandCheck(this, MessageEvent)\n\n    return this.#eventInit.origin\n  }\n\n  get lastEventId () {\n    webidl.brandCheck(this, MessageEvent)\n\n    return this.#eventInit.lastEventId\n  }\n\n  get source () {\n    webidl.brandCheck(this, MessageEvent)\n\n    return this.#eventInit.source\n  }\n\n  get ports () {\n    webidl.brandCheck(this, MessageEvent)\n\n    if (!Object.isFrozen(this.#eventInit.ports)) {\n      Object.freeze(this.#eventInit.ports)\n    }\n\n    return this.#eventInit.ports\n  }\n\n  initMessageEvent (\n    type,\n    bubbles = false,\n    cancelable = false,\n    data = null,\n    origin = '',\n    lastEventId = '',\n    source = null,\n    ports = []\n  ) {\n    webidl.brandCheck(this, MessageEvent)\n\n    webidl.argumentLengthCheck(arguments, 1, 'MessageEvent.initMessageEvent')\n\n    return new MessageEvent(type, {\n      bubbles, cancelable, data, origin, lastEventId, source, ports\n    })\n  }\n\n  static createFastMessageEvent (type, init) {\n    const messageEvent = new MessageEvent(kConstruct, type, init)\n    messageEvent.#eventInit = init\n    messageEvent.#eventInit.data ??= null\n    messageEvent.#eventInit.origin ??= ''\n    messageEvent.#eventInit.lastEventId ??= ''\n    messageEvent.#eventInit.source ??= null\n    messageEvent.#eventInit.ports ??= []\n    return messageEvent\n  }\n}\n\nconst { createFastMessageEvent } = MessageEvent\ndelete MessageEvent.createFastMessageEvent\n\n/**\n * @see https://websockets.spec.whatwg.org/#the-closeevent-interface\n */\nclass CloseEvent extends Event {\n  #eventInit\n\n  constructor (type, eventInitDict = {}) {\n    const prefix = 'CloseEvent constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    type = webidl.converters.DOMString(type, prefix, 'type')\n    eventInitDict = webidl.converters.CloseEventInit(eventInitDict)\n\n    super(type, eventInitDict)\n\n    this.#eventInit = eventInitDict\n    webidl.util.markAsUncloneable(this)\n  }\n\n  get wasClean () {\n    webidl.brandCheck(this, CloseEvent)\n\n    return this.#eventInit.wasClean\n  }\n\n  get code () {\n    webidl.brandCheck(this, CloseEvent)\n\n    return this.#eventInit.code\n  }\n\n  get reason () {\n    webidl.brandCheck(this, CloseEvent)\n\n    return this.#eventInit.reason\n  }\n}\n\n// https://html.spec.whatwg.org/multipage/webappapis.html#the-errorevent-interface\nclass ErrorEvent extends Event {\n  #eventInit\n\n  constructor (type, eventInitDict) {\n    const prefix = 'ErrorEvent constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    super(type, eventInitDict)\n    webidl.util.markAsUncloneable(this)\n\n    type = webidl.converters.DOMString(type, prefix, 'type')\n    eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {})\n\n    this.#eventInit = eventInitDict\n  }\n\n  get message () {\n    webidl.brandCheck(this, ErrorEvent)\n\n    return this.#eventInit.message\n  }\n\n  get filename () {\n    webidl.brandCheck(this, ErrorEvent)\n\n    return this.#eventInit.filename\n  }\n\n  get lineno () {\n    webidl.brandCheck(this, ErrorEvent)\n\n    return this.#eventInit.lineno\n  }\n\n  get colno () {\n    webidl.brandCheck(this, ErrorEvent)\n\n    return this.#eventInit.colno\n  }\n\n  get error () {\n    webidl.brandCheck(this, ErrorEvent)\n\n    return this.#eventInit.error\n  }\n}\n\nObject.defineProperties(MessageEvent.prototype, {\n  [Symbol.toStringTag]: {\n    value: 'MessageEvent',\n    configurable: true\n  },\n  data: kEnumerableProperty,\n  origin: kEnumerableProperty,\n  lastEventId: kEnumerableProperty,\n  source: kEnumerableProperty,\n  ports: kEnumerableProperty,\n  initMessageEvent: kEnumerableProperty\n})\n\nObject.defineProperties(CloseEvent.prototype, {\n  [Symbol.toStringTag]: {\n    value: 'CloseEvent',\n    configurable: true\n  },\n  reason: kEnumerableProperty,\n  code: kEnumerableProperty,\n  wasClean: kEnumerableProperty\n})\n\nObject.defineProperties(ErrorEvent.prototype, {\n  [Symbol.toStringTag]: {\n    value: 'ErrorEvent',\n    configurable: true\n  },\n  message: kEnumerableProperty,\n  filename: kEnumerableProperty,\n  lineno: kEnumerableProperty,\n  colno: kEnumerableProperty,\n  error: kEnumerableProperty\n})\n\nwebidl.converters.MessagePort = webidl.interfaceConverter(\n  webidl.is.MessagePort,\n  'MessagePort'\n)\n\nwebidl.converters['sequence<MessagePort>'] = webidl.sequenceConverter(\n  webidl.converters.MessagePort\n)\n\nconst eventInit = [\n  {\n    key: 'bubbles',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'cancelable',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'composed',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  }\n]\n\nwebidl.converters.MessageEventInit = webidl.dictionaryConverter([\n  ...eventInit,\n  {\n    key: 'data',\n    converter: webidl.converters.any,\n    defaultValue: () => null\n  },\n  {\n    key: 'origin',\n    converter: webidl.converters.USVString,\n    defaultValue: () => ''\n  },\n  {\n    key: 'lastEventId',\n    converter: webidl.converters.DOMString,\n    defaultValue: () => ''\n  },\n  {\n    key: 'source',\n    // Node doesn't implement WindowProxy or ServiceWorker, so the only\n    // valid value for source is a MessagePort.\n    converter: webidl.nullableConverter(webidl.converters.MessagePort),\n    defaultValue: () => null\n  },\n  {\n    key: 'ports',\n    converter: webidl.converters['sequence<MessagePort>'],\n    defaultValue: () => []\n  }\n])\n\nwebidl.converters.CloseEventInit = webidl.dictionaryConverter([\n  ...eventInit,\n  {\n    key: 'wasClean',\n    converter: webidl.converters.boolean,\n    defaultValue: () => false\n  },\n  {\n    key: 'code',\n    converter: webidl.converters['unsigned short'],\n    defaultValue: () => 0\n  },\n  {\n    key: 'reason',\n    converter: webidl.converters.USVString,\n    defaultValue: () => ''\n  }\n])\n\nwebidl.converters.ErrorEventInit = webidl.dictionaryConverter([\n  ...eventInit,\n  {\n    key: 'message',\n    converter: webidl.converters.DOMString,\n    defaultValue: () => ''\n  },\n  {\n    key: 'filename',\n    converter: webidl.converters.USVString,\n    defaultValue: () => ''\n  },\n  {\n    key: 'lineno',\n    converter: webidl.converters['unsigned long'],\n    defaultValue: () => 0\n  },\n  {\n    key: 'colno',\n    converter: webidl.converters['unsigned long'],\n    defaultValue: () => 0\n  },\n  {\n    key: 'error',\n    converter: webidl.converters.any\n  }\n])\n\nmodule.exports = {\n  MessageEvent,\n  CloseEvent,\n  ErrorEvent,\n  createFastMessageEvent\n}\n"
  },
  {
    "path": "lib/web/websocket/frame.js",
    "content": "'use strict'\n\nconst { runtimeFeatures } = require('../../util/runtime-features')\nconst { maxUnsigned16Bit, opcodes } = require('./constants')\n\nconst BUFFER_SIZE = 8 * 1024\n\nlet buffer = null\nlet bufIdx = BUFFER_SIZE\n\nconst randomFillSync = runtimeFeatures.has('crypto')\n  ? require('node:crypto').randomFillSync\n  // not full compatibility, but minimum.\n  : function randomFillSync (buffer, _offset, _size) {\n    for (let i = 0; i < buffer.length; ++i) {\n      buffer[i] = Math.random() * 255 | 0\n    }\n    return buffer\n  }\n\nfunction generateMask () {\n  if (bufIdx === BUFFER_SIZE) {\n    bufIdx = 0\n    randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)\n  }\n  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]\n}\n\nclass WebsocketFrameSend {\n  /**\n   * @param {Buffer|undefined} data\n   */\n  constructor (data) {\n    this.frameData = data\n  }\n\n  createFrame (opcode) {\n    const frameData = this.frameData\n    const maskKey = generateMask()\n    const bodyLength = frameData?.byteLength ?? 0\n\n    /** @type {number} */\n    let payloadLength = bodyLength // 0-125\n    let offset = 6\n\n    if (bodyLength > maxUnsigned16Bit) {\n      offset += 8 // payload length is next 8 bytes\n      payloadLength = 127\n    } else if (bodyLength > 125) {\n      offset += 2 // payload length is next 2 bytes\n      payloadLength = 126\n    }\n\n    const buffer = Buffer.allocUnsafe(bodyLength + offset)\n\n    // Clear first 2 bytes, everything else is overwritten\n    buffer[0] = buffer[1] = 0\n    buffer[0] |= 0x80 // FIN\n    buffer[0] = (buffer[0] & 0xF0) + opcode // opcode\n\n    /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */\n    buffer[offset - 4] = maskKey[0]\n    buffer[offset - 3] = maskKey[1]\n    buffer[offset - 2] = maskKey[2]\n    buffer[offset - 1] = maskKey[3]\n\n    buffer[1] = payloadLength\n\n    if (payloadLength === 126) {\n      buffer.writeUInt16BE(bodyLength, 2)\n    } else if (payloadLength === 127) {\n      // Clear extended payload length\n      buffer[2] = buffer[3] = 0\n      buffer.writeUIntBE(bodyLength, 4, 6)\n    }\n\n    buffer[1] |= 0x80 // MASK\n\n    // mask body\n    for (let i = 0; i < bodyLength; ++i) {\n      buffer[offset + i] = frameData[i] ^ maskKey[i & 3]\n    }\n\n    return buffer\n  }\n\n  /**\n   * @param {Uint8Array} buffer\n   */\n  static createFastTextFrame (buffer) {\n    const maskKey = generateMask()\n\n    const bodyLength = buffer.length\n\n    // mask body\n    for (let i = 0; i < bodyLength; ++i) {\n      buffer[i] ^= maskKey[i & 3]\n    }\n\n    let payloadLength = bodyLength\n    let offset = 6\n\n    if (bodyLength > maxUnsigned16Bit) {\n      offset += 8 // payload length is next 8 bytes\n      payloadLength = 127\n    } else if (bodyLength > 125) {\n      offset += 2 // payload length is next 2 bytes\n      payloadLength = 126\n    }\n    const head = Buffer.allocUnsafeSlow(offset)\n\n    head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */\n    head[1] = payloadLength | 0x80 /* MASK */\n    head[offset - 4] = maskKey[0]\n    head[offset - 3] = maskKey[1]\n    head[offset - 2] = maskKey[2]\n    head[offset - 1] = maskKey[3]\n\n    if (payloadLength === 126) {\n      head.writeUInt16BE(bodyLength, 2)\n    } else if (payloadLength === 127) {\n      head[2] = head[3] = 0\n      head.writeUIntBE(bodyLength, 4, 6)\n    }\n\n    return [head, buffer]\n  }\n}\n\nmodule.exports = {\n  WebsocketFrameSend,\n  generateMask // for benchmark\n}\n"
  },
  {
    "path": "lib/web/websocket/permessage-deflate.js",
    "content": "'use strict'\n\nconst { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib')\nconst { isValidClientWindowBits } = require('./util')\nconst { MessageSizeExceededError } = require('../../core/errors')\n\nconst tail = Buffer.from([0x00, 0x00, 0xff, 0xff])\nconst kBuffer = Symbol('kBuffer')\nconst kLength = Symbol('kLength')\n\n// Default maximum decompressed message size: 4 MB\nconst kDefaultMaxDecompressedSize = 4 * 1024 * 1024\n\nclass PerMessageDeflate {\n  /** @type {import('node:zlib').InflateRaw} */\n  #inflate\n\n  #options = {}\n\n  /** @type {boolean} */\n  #aborted = false\n\n  /** @type {Function|null} */\n  #currentCallback = null\n\n  /**\n   * @param {Map<string, string>} extensions\n   */\n  constructor (extensions) {\n    this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')\n    this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')\n  }\n\n  decompress (chunk, fin, callback) {\n    // An endpoint uses the following algorithm to decompress a message.\n    // 1.  Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the\n    //     payload of the message.\n    // 2.  Decompress the resulting data using DEFLATE.\n\n    if (this.#aborted) {\n      callback(new MessageSizeExceededError())\n      return\n    }\n\n    if (!this.#inflate) {\n      let windowBits = Z_DEFAULT_WINDOWBITS\n\n      if (this.#options.serverMaxWindowBits) { // empty values default to Z_DEFAULT_WINDOWBITS\n        if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {\n          callback(new Error('Invalid server_max_window_bits'))\n          return\n        }\n\n        windowBits = Number.parseInt(this.#options.serverMaxWindowBits)\n      }\n\n      try {\n        this.#inflate = createInflateRaw({ windowBits })\n      } catch (err) {\n        callback(err)\n        return\n      }\n      this.#inflate[kBuffer] = []\n      this.#inflate[kLength] = 0\n\n      this.#inflate.on('data', (data) => {\n        if (this.#aborted) {\n          return\n        }\n\n        this.#inflate[kLength] += data.length\n\n        if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {\n          this.#aborted = true\n          this.#inflate.removeAllListeners()\n          this.#inflate.destroy()\n          this.#inflate = null\n\n          if (this.#currentCallback) {\n            const cb = this.#currentCallback\n            this.#currentCallback = null\n            cb(new MessageSizeExceededError())\n          }\n          return\n        }\n\n        this.#inflate[kBuffer].push(data)\n      })\n\n      this.#inflate.on('error', (err) => {\n        this.#inflate = null\n        callback(err)\n      })\n    }\n\n    this.#currentCallback = callback\n    this.#inflate.write(chunk)\n    if (fin) {\n      this.#inflate.write(tail)\n    }\n\n    this.#inflate.flush(() => {\n      if (this.#aborted || !this.#inflate) {\n        return\n      }\n\n      const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])\n\n      this.#inflate[kBuffer].length = 0\n      this.#inflate[kLength] = 0\n      this.#currentCallback = null\n\n      callback(null, full)\n    })\n  }\n}\n\nmodule.exports = { PerMessageDeflate }\n"
  },
  {
    "path": "lib/web/websocket/receiver.js",
    "content": "'use strict'\n\nconst { Writable } = require('node:stream')\nconst assert = require('node:assert')\nconst { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')\nconst {\n  isValidStatusCode,\n  isValidOpcode,\n  websocketMessageReceived,\n  utf8Decode,\n  isControlFrame,\n  isTextBinaryFrame,\n  isContinuationFrame\n} = require('./util')\nconst { failWebsocketConnection } = require('./connection')\nconst { WebsocketFrameSend } = require('./frame')\nconst { PerMessageDeflate } = require('./permessage-deflate')\nconst { MessageSizeExceededError } = require('../../core/errors')\n\n// This code was influenced by ws released under the MIT license.\n// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>\n// Copyright (c) 2013 Arnout Kazemier and contributors\n// Copyright (c) 2016 Luigi Pinca and contributors\n\nclass ByteParser extends Writable {\n  #buffers = []\n  #fragmentsBytes = 0\n  #byteOffset = 0\n  #loop = false\n\n  #state = parserStates.INFO\n\n  #info = {}\n  #fragments = []\n\n  /** @type {Map<string, PerMessageDeflate>} */\n  #extensions\n\n  /** @type {import('./websocket').Handler} */\n  #handler\n\n  /**\n   * @param {import('./websocket').Handler} handler\n   * @param {Map<string, string>|null} extensions\n   */\n  constructor (handler, extensions) {\n    super()\n\n    this.#handler = handler\n    this.#extensions = extensions == null ? new Map() : extensions\n\n    if (this.#extensions.has('permessage-deflate')) {\n      this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))\n    }\n  }\n\n  /**\n   * @param {Buffer} chunk\n   * @param {() => void} callback\n   */\n  _write (chunk, _, callback) {\n    this.#buffers.push(chunk)\n    this.#byteOffset += chunk.length\n    this.#loop = true\n\n    this.run(callback)\n  }\n\n  /**\n   * Runs whenever a new chunk is received.\n   * Callback is called whenever there are no more chunks buffering,\n   * or not enough bytes are buffered to parse.\n   */\n  run (callback) {\n    while (this.#loop) {\n      if (this.#state === parserStates.INFO) {\n        // If there aren't enough bytes to parse the payload length, etc.\n        if (this.#byteOffset < 2) {\n          return callback()\n        }\n\n        const buffer = this.consume(2)\n        const fin = (buffer[0] & 0x80) !== 0\n        const opcode = buffer[0] & 0x0F\n        const masked = (buffer[1] & 0x80) === 0x80\n\n        const fragmented = !fin && opcode !== opcodes.CONTINUATION\n        const payloadLength = buffer[1] & 0x7F\n\n        const rsv1 = buffer[0] & 0x40\n        const rsv2 = buffer[0] & 0x20\n        const rsv3 = buffer[0] & 0x10\n\n        if (!isValidOpcode(opcode)) {\n          failWebsocketConnection(this.#handler, 1002, 'Invalid opcode received')\n          return callback()\n        }\n\n        if (masked) {\n          failWebsocketConnection(this.#handler, 1002, 'Frame cannot be masked')\n          return callback()\n        }\n\n        // MUST be 0 unless an extension is negotiated that defines meanings\n        // for non-zero values.  If a nonzero value is received and none of\n        // the negotiated extensions defines the meaning of such a nonzero\n        // value, the receiving endpoint MUST _Fail the WebSocket\n        // Connection_.\n        // This document allocates the RSV1 bit of the WebSocket header for\n        // PMCEs and calls the bit the \"Per-Message Compressed\" bit.  On a\n        // WebSocket connection where a PMCE is in use, this bit indicates\n        // whether a message is compressed or not.\n        if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {\n          failWebsocketConnection(this.#handler, 1002, 'Expected RSV1 to be clear.')\n          return\n        }\n\n        if (rsv2 !== 0 || rsv3 !== 0) {\n          failWebsocketConnection(this.#handler, 1002, 'RSV1, RSV2, RSV3 must be clear')\n          return\n        }\n\n        if (fragmented && !isTextBinaryFrame(opcode)) {\n          // Only text and binary frames can be fragmented\n          failWebsocketConnection(this.#handler, 1002, 'Invalid frame type was fragmented.')\n          return\n        }\n\n        // If we are already parsing a text/binary frame and do not receive either\n        // a continuation frame or close frame, fail the connection.\n        if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {\n          failWebsocketConnection(this.#handler, 1002, 'Expected continuation frame')\n          return\n        }\n\n        if (this.#info.fragmented && fragmented) {\n          // A fragmented frame can't be fragmented itself\n          failWebsocketConnection(this.#handler, 1002, 'Fragmented frame exceeded 125 bytes.')\n          return\n        }\n\n        // \"All control frames MUST have a payload length of 125 bytes or less\n        // and MUST NOT be fragmented.\"\n        if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {\n          failWebsocketConnection(this.#handler, 1002, 'Control frame either too large or fragmented')\n          return\n        }\n\n        if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {\n          failWebsocketConnection(this.#handler, 1002, 'Unexpected continuation frame')\n          return\n        }\n\n        if (payloadLength <= 125) {\n          this.#info.payloadLength = payloadLength\n          this.#state = parserStates.READ_DATA\n        } else if (payloadLength === 126) {\n          this.#state = parserStates.PAYLOADLENGTH_16\n        } else if (payloadLength === 127) {\n          this.#state = parserStates.PAYLOADLENGTH_64\n        }\n\n        if (isTextBinaryFrame(opcode)) {\n          this.#info.binaryType = opcode\n          this.#info.compressed = rsv1 !== 0\n        }\n\n        this.#info.opcode = opcode\n        this.#info.masked = masked\n        this.#info.fin = fin\n        this.#info.fragmented = fragmented\n      } else if (this.#state === parserStates.PAYLOADLENGTH_16) {\n        if (this.#byteOffset < 2) {\n          return callback()\n        }\n\n        const buffer = this.consume(2)\n\n        this.#info.payloadLength = buffer.readUInt16BE(0)\n        this.#state = parserStates.READ_DATA\n      } else if (this.#state === parserStates.PAYLOADLENGTH_64) {\n        if (this.#byteOffset < 8) {\n          return callback()\n        }\n\n        const buffer = this.consume(8)\n        const upper = buffer.readUInt32BE(0)\n        const lower = buffer.readUInt32BE(4)\n\n        // 2^31 is the maximum bytes an arraybuffer can contain\n        // on 32-bit systems. Although, on 64-bit systems, this is\n        // 2^53-1 bytes.\n        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length\n        // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275\n        // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e\n        if (upper !== 0 || lower > 2 ** 31 - 1) {\n          failWebsocketConnection(this.#handler, 1009, 'Received payload length > 2^31 bytes.')\n          return\n        }\n\n        this.#info.payloadLength = lower\n        this.#state = parserStates.READ_DATA\n      } else if (this.#state === parserStates.READ_DATA) {\n        if (this.#byteOffset < this.#info.payloadLength) {\n          return callback()\n        }\n\n        const body = this.consume(this.#info.payloadLength)\n\n        if (isControlFrame(this.#info.opcode)) {\n          this.#loop = this.parseControlFrame(body)\n          this.#state = parserStates.INFO\n        } else {\n          if (!this.#info.compressed) {\n            this.writeFragments(body)\n\n            // If the frame is not fragmented, a message has been received.\n            // If the frame is fragmented, it will terminate with a fin bit set\n            // and an opcode of 0 (continuation), therefore we handle that when\n            // parsing continuation frames, not here.\n            if (!this.#info.fragmented && this.#info.fin) {\n              websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())\n            }\n\n            this.#state = parserStates.INFO\n          } else {\n            this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {\n              if (error) {\n                // Use 1009 (Message Too Big) for decompression size limit errors\n                const code = error instanceof MessageSizeExceededError ? 1009 : 1007\n                failWebsocketConnection(this.#handler, code, error.message)\n                return\n              }\n\n              this.writeFragments(data)\n\n              if (!this.#info.fin) {\n                this.#state = parserStates.INFO\n                this.#loop = true\n                this.run(callback)\n                return\n              }\n\n              websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())\n\n              this.#loop = true\n              this.#state = parserStates.INFO\n              this.run(callback)\n            })\n\n            this.#loop = false\n            break\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Take n bytes from the buffered Buffers\n   * @param {number} n\n   * @returns {Buffer}\n   */\n  consume (n) {\n    if (n > this.#byteOffset) {\n      throw new Error('Called consume() before buffers satiated.')\n    } else if (n === 0) {\n      return emptyBuffer\n    }\n\n    this.#byteOffset -= n\n\n    const first = this.#buffers[0]\n\n    if (first.length > n) {\n      // replace with remaining buffer\n      this.#buffers[0] = first.subarray(n, first.length)\n      return first.subarray(0, n)\n    } else if (first.length === n) {\n      // prefect match\n      return this.#buffers.shift()\n    } else {\n      let offset = 0\n      // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.\n      const buffer = Buffer.allocUnsafeSlow(n)\n      while (offset !== n) {\n        const next = this.#buffers[0]\n        const length = next.length\n\n        if (length + offset === n) {\n          buffer.set(this.#buffers.shift(), offset)\n          break\n        } else if (length + offset > n) {\n          buffer.set(next.subarray(0, n - offset), offset)\n          this.#buffers[0] = next.subarray(n - offset)\n          break\n        } else {\n          buffer.set(this.#buffers.shift(), offset)\n          offset += length\n        }\n      }\n\n      return buffer\n    }\n  }\n\n  writeFragments (fragment) {\n    this.#fragmentsBytes += fragment.length\n    this.#fragments.push(fragment)\n  }\n\n  consumeFragments () {\n    const fragments = this.#fragments\n\n    if (fragments.length === 1) {\n      // single fragment\n      this.#fragmentsBytes = 0\n      return fragments.shift()\n    }\n\n    let offset = 0\n    // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.\n    const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes)\n\n    for (let i = 0; i < fragments.length; ++i) {\n      const buffer = fragments[i]\n      output.set(buffer, offset)\n      offset += buffer.length\n    }\n\n    this.#fragments = []\n    this.#fragmentsBytes = 0\n\n    return output\n  }\n\n  parseCloseBody (data) {\n    assert(data.length !== 1)\n\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5\n    /** @type {number|undefined} */\n    let code\n\n    if (data.length >= 2) {\n      // _The WebSocket Connection Close Code_ is\n      // defined as the status code (Section 7.4) contained in the first Close\n      // control frame received by the application\n      code = data.readUInt16BE(0)\n    }\n\n    if (code !== undefined && !isValidStatusCode(code)) {\n      return { code: 1002, reason: 'Invalid status code', error: true }\n    }\n\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6\n    /** @type {Buffer} */\n    let reason = data.subarray(2)\n\n    // Remove BOM\n    if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) {\n      reason = reason.subarray(3)\n    }\n\n    try {\n      reason = utf8Decode(reason)\n    } catch {\n      return { code: 1007, reason: 'Invalid UTF-8', error: true }\n    }\n\n    return { code, reason, error: false }\n  }\n\n  /**\n   * Parses control frames.\n   * @param {Buffer} body\n   */\n  parseControlFrame (body) {\n    const { opcode, payloadLength } = this.#info\n\n    if (opcode === opcodes.CLOSE) {\n      if (payloadLength === 1) {\n        failWebsocketConnection(this.#handler, 1002, 'Received close frame with a 1-byte body.')\n        return false\n      }\n\n      this.#info.closeInfo = this.parseCloseBody(body)\n\n      if (this.#info.closeInfo.error) {\n        const { code, reason } = this.#info.closeInfo\n\n        failWebsocketConnection(this.#handler, code, reason)\n        return false\n      }\n\n      // Upon receiving such a frame, the other peer sends a\n      // Close frame in response, if it hasn't already sent one.\n      if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {\n        // If an endpoint receives a Close frame and did not previously send a\n        // Close frame, the endpoint MUST send a Close frame in response.  (When\n        // sending a Close frame in response, the endpoint typically echos the\n        // status code it received.)\n        let body = emptyBuffer\n        if (this.#info.closeInfo.code) {\n          body = Buffer.allocUnsafe(2)\n          body.writeUInt16BE(this.#info.closeInfo.code, 0)\n        }\n        const closeFrame = new WebsocketFrameSend(body)\n\n        this.#handler.socket.write(closeFrame.createFrame(opcodes.CLOSE))\n        this.#handler.closeState.add(sentCloseFrameState.SENT)\n      }\n\n      // Upon either sending or receiving a Close control frame, it is said\n      // that _The WebSocket Closing Handshake is Started_ and that the\n      // WebSocket connection is in the CLOSING state.\n      this.#handler.readyState = states.CLOSING\n      this.#handler.closeState.add(sentCloseFrameState.RECEIVED)\n\n      return false\n    } else if (opcode === opcodes.PING) {\n      // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in\n      // response, unless it already received a Close frame.\n      // A Pong frame sent in response to a Ping frame must have identical\n      // \"Application data\"\n\n      if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {\n        const frame = new WebsocketFrameSend(body)\n\n        this.#handler.socket.write(frame.createFrame(opcodes.PONG))\n\n        this.#handler.onPing(body)\n      }\n    } else if (opcode === opcodes.PONG) {\n      // A Pong frame MAY be sent unsolicited.  This serves as a\n      // unidirectional heartbeat.  A response to an unsolicited Pong frame is\n      // not expected.\n      this.#handler.onPong(body)\n    }\n\n    return true\n  }\n\n  get closingInfo () {\n    return this.#info.closeInfo\n  }\n}\n\nmodule.exports = {\n  ByteParser\n}\n"
  },
  {
    "path": "lib/web/websocket/sender.js",
    "content": "'use strict'\n\nconst { WebsocketFrameSend } = require('./frame')\nconst { opcodes, sendHints } = require('./constants')\nconst FixedQueue = require('../../dispatcher/fixed-queue')\n\n/**\n * @typedef {object} SendQueueNode\n * @property {Promise<void> | null} promise\n * @property {((...args: any[]) => any)} callback\n * @property {Buffer | null} frame\n */\n\nclass SendQueue {\n  /**\n   * @type {FixedQueue}\n   */\n  #queue = new FixedQueue()\n\n  /**\n   * @type {boolean}\n   */\n  #running = false\n\n  /** @type {import('node:net').Socket} */\n  #socket\n\n  constructor (socket) {\n    this.#socket = socket\n  }\n\n  add (item, cb, hint) {\n    if (hint !== sendHints.blob) {\n      if (!this.#running) {\n        // TODO(@tsctx): support fast-path for string on running\n        if (hint === sendHints.text) {\n          // special fast-path for string\n          const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item)\n          this.#socket.cork()\n          this.#socket.write(head)\n          this.#socket.write(body, cb)\n          this.#socket.uncork()\n        } else {\n          // direct writing\n          this.#socket.write(createFrame(item, hint), cb)\n        }\n      } else {\n        /** @type {SendQueueNode} */\n        const node = {\n          promise: null,\n          callback: cb,\n          frame: createFrame(item, hint)\n        }\n        this.#queue.push(node)\n      }\n      return\n    }\n\n    /** @type {SendQueueNode} */\n    const node = {\n      promise: item.arrayBuffer().then((ab) => {\n        node.promise = null\n        node.frame = createFrame(ab, hint)\n      }),\n      callback: cb,\n      frame: null\n    }\n\n    this.#queue.push(node)\n\n    if (!this.#running) {\n      this.#run()\n    }\n  }\n\n  async #run () {\n    this.#running = true\n    const queue = this.#queue\n    while (!queue.isEmpty()) {\n      const node = queue.shift()\n      // wait pending promise\n      if (node.promise !== null) {\n        await node.promise\n      }\n      // write\n      this.#socket.write(node.frame, node.callback)\n      // cleanup\n      node.callback = node.frame = null\n    }\n    this.#running = false\n  }\n}\n\nfunction createFrame (data, hint) {\n  return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY)\n}\n\nfunction toBuffer (data, hint) {\n  switch (hint) {\n    case sendHints.text:\n    case sendHints.typedArray:\n      return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n    case sendHints.arrayBuffer:\n    case sendHints.blob:\n      return new Uint8Array(data)\n  }\n}\n\nmodule.exports = { SendQueue }\n"
  },
  {
    "path": "lib/web/websocket/stream/websocketerror.js",
    "content": "'use strict'\n\nconst { webidl } = require('../../webidl')\nconst { validateCloseCodeAndReason } = require('../util')\nconst { kConstruct } = require('../../../core/symbols')\nconst { kEnumerableProperty } = require('../../../core/util')\n\nfunction createInheritableDOMException () {\n  // https://github.com/nodejs/node/issues/59677\n  class Test extends DOMException {\n    get reason () {\n      return ''\n    }\n  }\n\n  if (new Test().reason !== undefined) {\n    return DOMException\n  }\n\n  return new Proxy(DOMException, {\n    construct (target, args, newTarget) {\n      const instance = Reflect.construct(target, args, target)\n      Object.setPrototypeOf(instance, newTarget.prototype)\n      return instance\n    }\n  })\n}\n\nclass WebSocketError extends createInheritableDOMException() {\n  #closeCode\n  #reason\n\n  constructor (message = '', init = undefined) {\n    message = webidl.converters.DOMString(message, 'WebSocketError', 'message')\n\n    // 1. Set this 's name to \" WebSocketError \".\n    // 2. Set this 's message to message .\n    super(message, 'WebSocketError')\n\n    if (init === kConstruct) {\n      return\n    } else if (init !== null) {\n      init = webidl.converters.WebSocketCloseInfo(init)\n    }\n\n    // 3. Let code be init [\" closeCode \"] if it exists , or null otherwise.\n    let code = init.closeCode ?? null\n\n    // 4. Let reason be init [\" reason \"] if it exists , or the empty string otherwise.\n    const reason = init.reason ?? ''\n\n    // 5. Validate close code and reason with code and reason .\n    validateCloseCodeAndReason(code, reason)\n\n    // 6. If reason is non-empty, but code is not set, then set code to 1000 (\"Normal Closure\").\n    if (reason.length !== 0 && code === null) {\n      code = 1000\n    }\n\n    // 7. Set this 's closeCode to code .\n    this.#closeCode = code\n\n    // 8. Set this 's reason to reason .\n    this.#reason = reason\n  }\n\n  get closeCode () {\n    return this.#closeCode\n  }\n\n  get reason () {\n    return this.#reason\n  }\n\n  /**\n   * @param {string} message\n   * @param {number|null} code\n   * @param {string} reason\n   */\n  static createUnvalidatedWebSocketError (message, code, reason) {\n    const error = new WebSocketError(message, kConstruct)\n    error.#closeCode = code\n    error.#reason = reason\n    return error\n  }\n}\n\nconst { createUnvalidatedWebSocketError } = WebSocketError\ndelete WebSocketError.createUnvalidatedWebSocketError\n\nObject.defineProperties(WebSocketError.prototype, {\n  closeCode: kEnumerableProperty,\n  reason: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'WebSocketError',\n    writable: false,\n    enumerable: false,\n    configurable: true\n  }\n})\n\nwebidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError)\n\nmodule.exports = { WebSocketError, createUnvalidatedWebSocketError }\n"
  },
  {
    "path": "lib/web/websocket/stream/websocketstream.js",
    "content": "'use strict'\n\nconst { createDeferredPromise } = require('../../../util/promise')\nconst { environmentSettingsObject } = require('../../fetch/util')\nconst { states, opcodes, sentCloseFrameState } = require('../constants')\nconst { webidl } = require('../../webidl')\nconst { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')\nconst { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')\nconst { channels } = require('../../../core/diagnostics')\nconst { WebsocketFrameSend } = require('../frame')\nconst { ByteParser } = require('../receiver')\nconst { WebSocketError, createUnvalidatedWebSocketError } = require('./websocketerror')\nconst { kEnumerableProperty } = require('../../../core/util')\nconst { utf8DecodeBytes } = require('../../../encoding')\n\nlet emittedExperimentalWarning = false\n\nclass WebSocketStream {\n  // Each WebSocketStream object has an associated url , which is a URL record .\n  /** @type {URL} */\n  #url\n\n  // Each WebSocketStream object has an associated opened promise , which is a promise.\n  /** @type {import('../../../util/promise').DeferredPromise} */\n  #openedPromise\n\n  // Each WebSocketStream object has an associated closed promise , which is a promise.\n  /** @type {import('../../../util/promise').DeferredPromise} */\n  #closedPromise\n\n  // Each WebSocketStream object has an associated readable stream , which is a ReadableStream .\n  /** @type {ReadableStream} */\n  #readableStream\n  /** @type {ReadableStreamDefaultController} */\n  #readableStreamController\n\n  // Each WebSocketStream object has an associated writable stream , which is a WritableStream .\n  /** @type {WritableStream} */\n  #writableStream\n\n  // Each WebSocketStream object has an associated boolean handshake aborted , which is initially false.\n  #handshakeAborted = false\n\n  /** @type {import('../websocket').Handler} */\n  #handler = {\n    // https://whatpr.org/websockets/48/7b748d3...d5570f3.html#feedback-to-websocket-stream-from-the-protocol\n    onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),\n    onMessage: (opcode, data) => this.#onMessage(opcode, data),\n    onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),\n    onParserDrain: () => this.#handler.socket.resume(),\n    onSocketData: (chunk) => {\n      if (!this.#parser.write(chunk)) {\n        this.#handler.socket.pause()\n      }\n    },\n    onSocketError: (err) => {\n      this.#handler.readyState = states.CLOSING\n\n      if (channels.socketError.hasSubscribers) {\n        channels.socketError.publish(err)\n      }\n\n      this.#handler.socket.destroy()\n    },\n    onSocketClose: () => this.#onSocketClose(),\n    onPing: () => {},\n    onPong: () => {},\n\n    readyState: states.CONNECTING,\n    socket: null,\n    closeState: new Set(),\n    controller: null,\n    wasEverConnected: false\n  }\n\n  /** @type {import('../receiver').ByteParser} */\n  #parser\n\n  constructor (url, options = undefined) {\n    if (!emittedExperimentalWarning) {\n      process.emitWarning('WebSocketStream is experimental! Expect it to change at any time.', {\n        code: 'UNDICI-WSS'\n      })\n      emittedExperimentalWarning = true\n    }\n\n    webidl.argumentLengthCheck(arguments, 1, 'WebSocket')\n\n    url = webidl.converters.USVString(url)\n    if (options !== null) {\n      options = webidl.converters.WebSocketStreamOptions(options)\n    }\n\n    // 1. Let baseURL be this 's relevant settings object 's API base URL .\n    const baseURL = environmentSettingsObject.settingsObject.baseUrl\n\n    // 2. Let urlRecord be the result of getting a URL record given url and baseURL .\n    const urlRecord = getURLRecord(url, baseURL)\n\n    // 3. Let protocols be options [\" protocols \"] if it exists , otherwise an empty sequence.\n    const protocols = options.protocols\n\n    // 4. If any of the values in protocols occur more than once or otherwise fail to match the requirements for elements that comprise the value of ` Sec-WebSocket-Protocol ` fields as defined by The WebSocket Protocol , then throw a \" SyntaxError \" DOMException . [WSP]\n    if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {\n      throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n    }\n\n    if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {\n      throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n    }\n\n    // 5. Set this 's url to urlRecord .\n    this.#url = urlRecord.toString()\n\n    // 6. Set this 's opened promise and closed promise to new promises.\n    this.#openedPromise = createDeferredPromise()\n    this.#closedPromise = createDeferredPromise()\n\n    // 7. Apply backpressure to the WebSocket.\n    // TODO\n\n    // 8.  If options [\" signal \"] exists ,\n    if (options.signal != null) {\n      // 8.1. Let signal be options [\" signal \"].\n      const signal = options.signal\n\n      // 8.2. If signal is aborted , then reject this 's opened promise and closed promise with signal ’s abort reason\n      //      and return.\n      if (signal.aborted) {\n        this.#openedPromise.reject(signal.reason)\n        this.#closedPromise.reject(signal.reason)\n        return\n      }\n\n      // 8.3. Add the following abort steps to signal :\n      signal.addEventListener('abort', () => {\n        // 8.3.1. If the WebSocket connection is not yet established : [WSP]\n        if (!isEstablished(this.#handler.readyState)) {\n          // 8.3.1.1. Fail the WebSocket connection .\n          failWebsocketConnection(this.#handler)\n\n          // Set this 's ready state to CLOSING .\n          this.#handler.readyState = states.CLOSING\n\n          // Reject this 's opened promise and closed promise with signal ’s abort reason .\n          this.#openedPromise.reject(signal.reason)\n          this.#closedPromise.reject(signal.reason)\n\n          // Set this 's handshake aborted to true.\n          this.#handshakeAborted = true\n        }\n      }, { once: true })\n    }\n\n    // 9.  Let client be this 's relevant settings object .\n    const client = environmentSettingsObject.settingsObject\n\n    // 10. Run this step in parallel :\n    // 10.1. Establish a WebSocket connection given urlRecord , protocols , and client . [FETCH]\n    this.#handler.controller = establishWebSocketConnection(\n      urlRecord,\n      protocols,\n      client,\n      this.#handler,\n      options\n    )\n  }\n\n  // The url getter steps are to return this 's url , serialized .\n  get url () {\n    return this.#url.toString()\n  }\n\n  // The opened getter steps are to return this 's opened promise .\n  get opened () {\n    return this.#openedPromise.promise\n  }\n\n  // The closed getter steps are to return this 's closed promise .\n  get closed () {\n    return this.#closedPromise.promise\n  }\n\n  // The close( closeInfo ) method steps are:\n  close (closeInfo = undefined) {\n    if (closeInfo !== null) {\n      closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo)\n    }\n\n    // 1. Let code be closeInfo [\" closeCode \"] if present, or null otherwise.\n    const code = closeInfo.closeCode ?? null\n\n    // 2. Let reason be closeInfo [\" reason \"].\n    const reason = closeInfo.reason\n\n    // 3. Close the WebSocket with this , code , and reason .\n    closeWebSocketConnection(this.#handler, code, reason, true)\n  }\n\n  #write (chunk) {\n    // See /websockets/stream/tentative/write.any.html\n    chunk = webidl.converters.WebSocketStreamWrite(chunk)\n\n    // 1. Let promise be a new promise created in stream ’s relevant realm .\n    const promise = createDeferredPromise()\n\n    // 2. Let data be null.\n    let data = null\n\n    // 3. Let opcode be null.\n    let opcode = null\n\n    // 4. If chunk is a BufferSource ,\n    if (webidl.is.BufferSource(chunk)) {\n      // 4.1. Set data to a copy of the bytes given chunk .\n      data = new Uint8Array(ArrayBuffer.isView(chunk) ? new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength) : chunk.slice())\n\n      // 4.2. Set opcode to a binary frame opcode.\n      opcode = opcodes.BINARY\n    } else {\n      // 5. Otherwise,\n\n      // 5.1. Let string be the result of converting chunk to an IDL USVString .\n      //    If this throws an exception, return a promise rejected with the exception.\n      let string\n\n      try {\n        string = webidl.converters.DOMString(chunk)\n      } catch (e) {\n        promise.reject(e)\n        return promise.promise\n      }\n\n      // 5.2. Set data to the result of UTF-8 encoding string .\n      data = new TextEncoder().encode(string)\n\n      // 5.3. Set opcode to a text frame opcode.\n      opcode = opcodes.TEXT\n    }\n\n    // 6. In parallel,\n    // 6.1. Wait until there is sufficient buffer space in stream to send the message.\n\n    // 6.2. If the closing handshake has not yet started , Send a WebSocket Message to stream comprised of data using opcode .\n    if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {\n      const frame = new WebsocketFrameSend(data)\n\n      this.#handler.socket.write(frame.createFrame(opcode), () => {\n        promise.resolve(undefined)\n      })\n    }\n\n    // 6.3. Queue a global task on the WebSocket task source given stream ’s relevant global object to resolve promise with undefined.\n    return promise.promise\n  }\n\n  /** @type {import('../websocket').Handler['onConnectionEstablished']} */\n  #onConnectionEstablished (response, parsedExtensions) {\n    this.#handler.socket = response.socket\n\n    const parser = new ByteParser(this.#handler, parsedExtensions)\n    parser.on('drain', () => this.#handler.onParserDrain())\n    parser.on('error', (err) => this.#handler.onParserError(err))\n\n    this.#parser = parser\n\n    // 1. Change stream ’s ready state to OPEN (1).\n    this.#handler.readyState = states.OPEN\n\n    // 2. Set stream ’s was ever connected to true.\n    // This is done in the opening handshake.\n\n    // 3. Let extensions be the extensions in use .\n    const extensions = parsedExtensions ?? ''\n\n    // 4. Let protocol be the subprotocol in use .\n    const protocol = response.headersList.get('sec-websocket-protocol') ?? ''\n\n    // 5. Let pullAlgorithm be an action that pulls bytes from stream .\n    // 6. Let cancelAlgorithm be an action that cancels stream with reason , given reason .\n    // 7. Let readable be a new ReadableStream .\n    // 8. Set up readable with pullAlgorithm and cancelAlgorithm .\n    const readable = new ReadableStream({\n      start: (controller) => {\n        this.#readableStreamController = controller\n      },\n      pull (controller) {\n        let chunk\n        while (controller.desiredSize > 0 && (chunk = response.socket.read()) !== null) {\n          controller.enqueue(chunk)\n        }\n      },\n      cancel: (reason) => this.#cancel(reason)\n    })\n\n    // 9. Let writeAlgorithm be an action that writes chunk to stream , given chunk .\n    // 10. Let closeAlgorithm be an action that closes stream .\n    // 11. Let abortAlgorithm be an action that aborts stream with reason , given reason .\n    // 12. Let writable be a new WritableStream .\n    // 13. Set up writable with writeAlgorithm , closeAlgorithm , and abortAlgorithm .\n    const writable = new WritableStream({\n      write: (chunk) => this.#write(chunk),\n      close: () => closeWebSocketConnection(this.#handler, null, null),\n      abort: (reason) => this.#closeUsingReason(reason)\n    })\n\n    // Set stream ’s readable stream to readable .\n    this.#readableStream = readable\n\n    // Set stream ’s writable stream to writable .\n    this.#writableStream = writable\n\n    // Resolve stream ’s opened promise with WebSocketOpenInfo «[ \" extensions \" → extensions , \" protocol \" → protocol , \" readable \" → readable , \" writable \" → writable ]».\n    this.#openedPromise.resolve({\n      extensions,\n      protocol,\n      readable,\n      writable\n    })\n  }\n\n  /** @type {import('../websocket').Handler['onMessage']} */\n  #onMessage (type, data) {\n    // 1. If stream’s ready state is not OPEN (1), then return.\n    if (this.#handler.readyState !== states.OPEN) {\n      return\n    }\n\n    // 2. Let chunk be determined by switching on type:\n    //      - type indicates that the data is Text\n    //          a new DOMString containing data\n    //      - type indicates that the data is Binary\n    //          a new Uint8Array object, created in the relevant Realm of the\n    //          WebSocketStream object, whose contents are data\n    let chunk\n\n    if (type === opcodes.TEXT) {\n      try {\n        chunk = utf8Decode(data)\n      } catch {\n        failWebsocketConnection(this.#handler, 'Received invalid UTF-8 in text frame.')\n        return\n      }\n    } else if (type === opcodes.BINARY) {\n      chunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n    }\n\n    // 3. Enqueue chunk into stream’s readable stream.\n    this.#readableStreamController.enqueue(chunk)\n\n    // 4. Apply backpressure to the WebSocket.\n  }\n\n  /** @type {import('../websocket').Handler['onSocketClose']} */\n  #onSocketClose () {\n    const wasClean =\n      this.#handler.closeState.has(sentCloseFrameState.SENT) &&\n      this.#handler.closeState.has(sentCloseFrameState.RECEIVED)\n\n    // 1. Change the ready state to CLOSED (3).\n    this.#handler.readyState = states.CLOSED\n\n    // 2. If stream ’s handshake aborted is true, then return.\n    if (this.#handshakeAborted) {\n      return\n    }\n\n    // 3. If stream ’s was ever connected is false, then reject stream ’s opened promise with a new WebSocketError.\n    if (!this.#handler.wasEverConnected) {\n      this.#openedPromise.reject(new WebSocketError('Socket never opened'))\n    }\n\n    const result = this.#parser?.closingInfo\n\n    // 4. Let code be the WebSocket connection close code .\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5\n    // If this Close control frame contains no status code, _The WebSocket\n    // Connection Close Code_ is considered to be 1005. If _The WebSocket\n    // Connection is Closed_ and no Close control frame was received by the\n    // endpoint (such as could occur if the underlying transport connection\n    // is lost), _The WebSocket Connection Close Code_ is considered to be\n    // 1006.\n    let code = result?.code ?? 1005\n\n    if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {\n      code = 1006\n    }\n\n    // 5. Let reason be the result of applying UTF-8 decode without BOM to the WebSocket connection close reason .\n    const reason = result?.reason == null ? '' : utf8DecodeBytes(Buffer.from(result.reason))\n\n    // 6. If the connection was closed cleanly ,\n    if (wasClean) {\n      // 6.1. Close stream ’s readable stream .\n      this.#readableStreamController.close()\n\n      // 6.2. Error stream ’s writable stream with an \" InvalidStateError \" DOMException indicating that a closed WebSocketStream cannot be written to.\n      if (!this.#writableStream.locked) {\n        this.#writableStream.abort(new DOMException('A closed WebSocketStream cannot be written to', 'InvalidStateError'))\n      }\n\n      // 6.3. Resolve stream ’s closed promise with WebSocketCloseInfo «[ \" closeCode \" → code , \" reason \" → reason ]».\n      this.#closedPromise.resolve({\n        closeCode: code,\n        reason\n      })\n    } else {\n      // 7. Otherwise,\n\n      // 7.1. Let error be a new WebSocketError whose closeCode is code and reason is reason .\n      const error = createUnvalidatedWebSocketError('unclean close', code, reason)\n\n      // 7.2. Error stream ’s readable stream with error .\n      this.#readableStreamController?.error(error)\n\n      // 7.3. Error stream ’s writable stream with error .\n      this.#writableStream?.abort(error)\n\n      // 7.4. Reject stream ’s closed promise with error .\n      this.#closedPromise.reject(error)\n    }\n  }\n\n  #closeUsingReason (reason) {\n    // 1. Let code be null.\n    let code = null\n\n    // 2. Let reasonString be the empty string.\n    let reasonString = ''\n\n    // 3. If reason implements WebSocketError ,\n    if (webidl.is.WebSocketError(reason)) {\n      // 3.1. Set code to reason ’s closeCode .\n      code = reason.closeCode\n\n      // 3.2. Set reasonString to reason ’s reason .\n      reasonString = reason.reason\n    }\n\n    // 4. Close the WebSocket with stream , code , and reasonString . If this throws an exception,\n    //    discard code and reasonString and close the WebSocket with stream .\n    closeWebSocketConnection(this.#handler, code, reasonString)\n  }\n\n  //  To cancel a WebSocketStream stream given reason , close using reason giving stream and reason .\n  #cancel (reason) {\n    this.#closeUsingReason(reason)\n  }\n}\n\nObject.defineProperties(WebSocketStream.prototype, {\n  url: kEnumerableProperty,\n  opened: kEnumerableProperty,\n  closed: kEnumerableProperty,\n  close: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'WebSocketStream',\n    writable: false,\n    enumerable: false,\n    configurable: true\n  }\n})\n\nwebidl.converters.WebSocketStreamOptions = webidl.dictionaryConverter([\n  {\n    key: 'protocols',\n    converter: webidl.sequenceConverter(webidl.converters.USVString),\n    defaultValue: () => []\n  },\n  {\n    key: 'signal',\n    converter: webidl.nullableConverter(webidl.converters.AbortSignal),\n    defaultValue: () => null\n  }\n])\n\nwebidl.converters.WebSocketCloseInfo = webidl.dictionaryConverter([\n  {\n    key: 'closeCode',\n    converter: (V) => webidl.converters['unsigned short'](V, webidl.attributes.EnforceRange)\n  },\n  {\n    key: 'reason',\n    converter: webidl.converters.USVString,\n    defaultValue: () => ''\n  }\n])\n\nwebidl.converters.WebSocketStreamWrite = function (V) {\n  if (typeof V === 'string') {\n    return webidl.converters.USVString(V)\n  }\n\n  return webidl.converters.BufferSource(V)\n}\n\nmodule.exports = { WebSocketStream }\n"
  },
  {
    "path": "lib/web/websocket/util.js",
    "content": "'use strict'\n\nconst { states, opcodes } = require('./constants')\nconst { isUtf8 } = require('node:buffer')\nconst { removeHTTPWhitespace } = require('../fetch/data-url')\nconst { collectASequenceOfCodePointsFast } = require('../infra')\n\n/**\n * @param {number} readyState\n * @returns {boolean}\n */\nfunction isConnecting (readyState) {\n  // If the WebSocket connection is not yet established, and the connection\n  // is not yet closed, then the WebSocket connection is in the CONNECTING state.\n  return readyState === states.CONNECTING\n}\n\n/**\n * @param {number} readyState\n * @returns {boolean}\n */\nfunction isEstablished (readyState) {\n  // If the server's response is validated as provided for above, it is\n  // said that _The WebSocket Connection is Established_ and that the\n  // WebSocket Connection is in the OPEN state.\n  return readyState === states.OPEN\n}\n\n/**\n * @param {number} readyState\n * @returns {boolean}\n */\nfunction isClosing (readyState) {\n  // Upon either sending or receiving a Close control frame, it is said\n  // that _The WebSocket Closing Handshake is Started_ and that the\n  // WebSocket connection is in the CLOSING state.\n  return readyState === states.CLOSING\n}\n\n/**\n * @param {number} readyState\n * @returns {boolean}\n */\nfunction isClosed (readyState) {\n  return readyState === states.CLOSED\n}\n\n/**\n * @see https://dom.spec.whatwg.org/#concept-event-fire\n * @param {string} e\n * @param {EventTarget} target\n * @param {(...args: ConstructorParameters<typeof Event>) => Event} eventFactory\n * @param {EventInit | undefined} eventInitDict\n * @returns {void}\n */\nfunction fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {\n  // 1. If eventConstructor is not given, then let eventConstructor be Event.\n\n  // 2. Let event be the result of creating an event given eventConstructor,\n  //    in the relevant realm of target.\n  // 3. Initialize event’s type attribute to e.\n  const event = eventFactory(e, eventInitDict)\n\n  // 4. Initialize any other IDL attributes of event as described in the\n  //    invocation of this algorithm.\n\n  // 5. Return the result of dispatching event at target, with legacy target\n  //    override flag set if set.\n  target.dispatchEvent(event)\n}\n\n/**\n * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol\n * @param {import('./websocket').Handler} handler\n * @param {number} type Opcode\n * @param {Buffer} data application data\n * @returns {void}\n */\nfunction websocketMessageReceived (handler, type, data) {\n  handler.onMessage(type, data)\n}\n\n/**\n * @param {Buffer} buffer\n * @returns {ArrayBuffer}\n */\nfunction toArrayBuffer (buffer) {\n  if (buffer.byteLength === buffer.buffer.byteLength) {\n    return buffer.buffer\n  }\n  return new Uint8Array(buffer).buffer\n}\n\n/**\n * @see https://datatracker.ietf.org/doc/html/rfc6455\n * @see https://datatracker.ietf.org/doc/html/rfc2616\n * @see https://bugs.chromium.org/p/chromium/issues/detail?id=398407\n * @param {string} protocol\n * @returns {boolean}\n */\nfunction isValidSubprotocol (protocol) {\n  // If present, this value indicates one\n  // or more comma-separated subprotocol the client wishes to speak,\n  // ordered by preference.  The elements that comprise this value\n  // MUST be non-empty strings with characters in the range U+0021 to\n  // U+007E not including separator characters as defined in\n  // [RFC2616] and MUST all be unique strings.\n  if (protocol.length === 0) {\n    return false\n  }\n\n  for (let i = 0; i < protocol.length; ++i) {\n    const code = protocol.charCodeAt(i)\n\n    if (\n      code < 0x21 || // CTL, contains SP (0x20) and HT (0x09)\n      code > 0x7E ||\n      code === 0x22 || // \"\n      code === 0x28 || // (\n      code === 0x29 || // )\n      code === 0x2C || // ,\n      code === 0x2F || // /\n      code === 0x3A || // :\n      code === 0x3B || // ;\n      code === 0x3C || // <\n      code === 0x3D || // =\n      code === 0x3E || // >\n      code === 0x3F || // ?\n      code === 0x40 || // @\n      code === 0x5B || // [\n      code === 0x5C || // \\\n      code === 0x5D || // ]\n      code === 0x7B || // {\n      code === 0x7D // }\n    ) {\n      return false\n    }\n  }\n\n  return true\n}\n\n/**\n * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7-4\n * @param {number} code\n * @returns {boolean}\n */\nfunction isValidStatusCode (code) {\n  if (code >= 1000 && code < 1015) {\n    return (\n      code !== 1004 && // reserved\n      code !== 1005 && // \"MUST NOT be set as a status code\"\n      code !== 1006 // \"MUST NOT be set as a status code\"\n    )\n  }\n\n  return code >= 3000 && code <= 4999\n}\n\n/**\n * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.5\n * @param {number} opcode\n * @returns {boolean}\n */\nfunction isControlFrame (opcode) {\n  return (\n    opcode === opcodes.CLOSE ||\n    opcode === opcodes.PING ||\n    opcode === opcodes.PONG\n  )\n}\n\n/**\n * @param {number} opcode\n * @returns {boolean}\n */\nfunction isContinuationFrame (opcode) {\n  return opcode === opcodes.CONTINUATION\n}\n\n/**\n * @param {number} opcode\n * @returns {boolean}\n */\nfunction isTextBinaryFrame (opcode) {\n  return opcode === opcodes.TEXT || opcode === opcodes.BINARY\n}\n\n/**\n *\n * @param {number} opcode\n * @returns {boolean}\n */\nfunction isValidOpcode (opcode) {\n  return isTextBinaryFrame(opcode) || isContinuationFrame(opcode) || isControlFrame(opcode)\n}\n\n/**\n * Parses a Sec-WebSocket-Extensions header value.\n * @param {string} extensions\n * @returns {Map<string, string>}\n */\n// TODO(@Uzlopak, @KhafraDev): make compliant https://datatracker.ietf.org/doc/html/rfc6455#section-9.1\nfunction parseExtensions (extensions) {\n  const position = { position: 0 }\n  const extensionList = new Map()\n\n  while (position.position < extensions.length) {\n    const pair = collectASequenceOfCodePointsFast(';', extensions, position)\n    const [name, value = ''] = pair.split('=', 2)\n\n    extensionList.set(\n      removeHTTPWhitespace(name, true, false),\n      removeHTTPWhitespace(value, false, true)\n    )\n\n    position.position++\n  }\n\n  return extensionList\n}\n\n/**\n * @see https://www.rfc-editor.org/rfc/rfc7692#section-7.1.2.2\n * @description \"client-max-window-bits = 1*DIGIT\"\n * @param {string} value\n * @returns {boolean}\n */\nfunction isValidClientWindowBits (value) {\n  // Must have at least one character\n  if (value.length === 0) {\n    return false\n  }\n\n  // Check all characters are ASCII digits\n  for (let i = 0; i < value.length; i++) {\n    const byte = value.charCodeAt(i)\n\n    if (byte < 0x30 || byte > 0x39) {\n      return false\n    }\n  }\n\n  // Check numeric range: zlib requires windowBits in range 8-15\n  const num = Number.parseInt(value, 10)\n  return num >= 8 && num <= 15\n}\n\n/**\n * @see https://whatpr.org/websockets/48/7b748d3...d5570f3.html#get-a-url-record\n * @param {string} url\n * @param {string} [baseURL]\n */\nfunction getURLRecord (url, baseURL) {\n  // 1. Let urlRecord be the result of applying the URL parser to url with baseURL .\n  // 2. If urlRecord is failure, then throw a \" SyntaxError \" DOMException .\n  let urlRecord\n\n  try {\n    urlRecord = new URL(url, baseURL)\n  } catch (e) {\n    throw new DOMException(e, 'SyntaxError')\n  }\n\n  // 3. If urlRecord ’s scheme is \" http \", then set urlRecord ’s scheme to \" ws \".\n  // 4. Otherwise, if urlRecord ’s scheme is \" https \", set urlRecord ’s scheme to \" wss \".\n  if (urlRecord.protocol === 'http:') {\n    urlRecord.protocol = 'ws:'\n  } else if (urlRecord.protocol === 'https:') {\n    urlRecord.protocol = 'wss:'\n  }\n\n  // 5. If urlRecord ’s scheme is not \" ws \" or \" wss \", then throw a \" SyntaxError \" DOMException .\n  if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {\n    throw new DOMException('expected a ws: or wss: url', 'SyntaxError')\n  }\n\n  // If urlRecord ’s fragment is non-null, then throw a \" SyntaxError \" DOMException .\n  if (urlRecord.hash.length || urlRecord.href.endsWith('#')) {\n    throw new DOMException('hash', 'SyntaxError')\n  }\n\n  // Return urlRecord .\n  return urlRecord\n}\n\n// https://whatpr.org/websockets/48.html#validate-close-code-and-reason\nfunction validateCloseCodeAndReason (code, reason) {\n  // 1. If code is not null, but is neither an integer equal to\n  //    1000 nor an integer in the range 3000 to 4999, inclusive,\n  //    throw an \"InvalidAccessError\" DOMException.\n  if (code !== null) {\n    if (code !== 1000 && (code < 3000 || code > 4999)) {\n      throw new DOMException('invalid code', 'InvalidAccessError')\n    }\n  }\n\n  // 2. If reason is not null, then:\n  if (reason !== null) {\n    // 2.1. Let reasonBytes be the result of UTF-8 encoding reason.\n    // 2.2. If reasonBytes is longer than 123 bytes, then throw a\n    //      \"SyntaxError\" DOMException.\n    const reasonBytesLength = Buffer.byteLength(reason)\n\n    if (reasonBytesLength > 123) {\n      throw new DOMException(`Reason must be less than 123 bytes; received ${reasonBytesLength}`, 'SyntaxError')\n    }\n  }\n}\n\n/**\n * Converts a Buffer to utf-8, even on platforms without icu.\n * @type {(buffer: Buffer) => string}\n */\nconst utf8Decode = (() => {\n  if (typeof process.versions.icu === 'string') {\n    const fatalDecoder = new TextDecoder('utf-8', { fatal: true })\n    return fatalDecoder.decode.bind(fatalDecoder)\n  }\n  return function (buffer) {\n    if (isUtf8(buffer)) {\n      return buffer.toString('utf-8')\n    }\n    throw new TypeError('Invalid utf-8 received.')\n  }\n})()\n\nmodule.exports = {\n  isConnecting,\n  isEstablished,\n  isClosing,\n  isClosed,\n  fireEvent,\n  isValidSubprotocol,\n  isValidStatusCode,\n  websocketMessageReceived,\n  utf8Decode,\n  isControlFrame,\n  isContinuationFrame,\n  isTextBinaryFrame,\n  isValidOpcode,\n  parseExtensions,\n  isValidClientWindowBits,\n  toArrayBuffer,\n  getURLRecord,\n  validateCloseCodeAndReason\n}\n"
  },
  {
    "path": "lib/web/websocket/websocket.js",
    "content": "'use strict'\n\nconst { isArrayBuffer } = require('node:util/types')\nconst { webidl } = require('../webidl')\nconst { URLSerializer } = require('../fetch/data-url')\nconst { environmentSettingsObject } = require('../fetch/util')\nconst { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes } = require('./constants')\nconst {\n  isConnecting,\n  isEstablished,\n  isClosing,\n  isClosed,\n  isValidSubprotocol,\n  fireEvent,\n  utf8Decode,\n  toArrayBuffer,\n  getURLRecord\n} = require('./util')\nconst { establishWebSocketConnection, closeWebSocketConnection, failWebsocketConnection } = require('./connection')\nconst { ByteParser } = require('./receiver')\nconst { kEnumerableProperty } = require('../../core/util')\nconst { getGlobalDispatcher } = require('../../global')\nconst { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events')\nconst { SendQueue } = require('./sender')\nconst { WebsocketFrameSend } = require('./frame')\nconst { channels } = require('../../core/diagnostics')\n\n/**\n * @typedef {object} Handler\n * @property {(response: any, extensions?: string[]) => void} onConnectionEstablished\n * @property {(opcode: number, data: Buffer) => void} onMessage\n * @property {(error: Error) => void} onParserError\n * @property {() => void} onParserDrain\n * @property {(chunk: Buffer) => void} onSocketData\n * @property {(err: Error) => void} onSocketError\n * @property {() => void} onSocketClose\n * @property {(body: Buffer) => void} onPing\n * @property {(body: Buffer) => void} onPong\n *\n * @property {number} readyState\n * @property {import('stream').Duplex} socket\n * @property {Set<number>} closeState\n * @property {import('../fetch/index').Fetch} controller\n * @property {boolean} [wasEverConnected=false]\n */\n\n// https://websockets.spec.whatwg.org/#interface-definition\nclass WebSocket extends EventTarget {\n  #events = {\n    open: null,\n    error: null,\n    close: null,\n    message: null\n  }\n\n  #bufferedAmount = 0\n  #protocol = ''\n  #extensions = ''\n\n  /** @type {SendQueue} */\n  #sendQueue\n\n  /** @type {Handler} */\n  #handler = {\n    onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),\n    onMessage: (opcode, data) => this.#onMessage(opcode, data),\n    onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),\n    onParserDrain: () => this.#onParserDrain(),\n    onSocketData: (chunk) => {\n      if (!this.#parser.write(chunk)) {\n        this.#handler.socket.pause()\n      }\n    },\n    onSocketError: (err) => {\n      this.#handler.readyState = states.CLOSING\n\n      if (channels.socketError.hasSubscribers) {\n        channels.socketError.publish(err)\n      }\n\n      this.#handler.socket.destroy()\n    },\n    onSocketClose: () => this.#onSocketClose(),\n    onPing: (body) => {\n      if (channels.ping.hasSubscribers) {\n        channels.ping.publish({\n          payload: body,\n          websocket: this\n        })\n      }\n    },\n    onPong: (body) => {\n      if (channels.pong.hasSubscribers) {\n        channels.pong.publish({\n          payload: body,\n          websocket: this\n        })\n      }\n    },\n\n    readyState: states.CONNECTING,\n    socket: null,\n    closeState: new Set(),\n    controller: null,\n    wasEverConnected: false\n  }\n\n  #url\n  #binaryType\n  /** @type {import('./receiver').ByteParser} */\n  #parser\n\n  /**\n   * @param {string} url\n   * @param {string|string[]} protocols\n   */\n  constructor (url, protocols = []) {\n    super()\n\n    webidl.util.markAsUncloneable(this)\n\n    const prefix = 'WebSocket constructor'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols, prefix, 'options')\n\n    url = webidl.converters.USVString(url)\n    protocols = options.protocols\n\n    // 1. Let baseURL be this's relevant settings object's API base URL.\n    const baseURL = environmentSettingsObject.settingsObject.baseUrl\n\n    // 2. Let urlRecord be the result of getting a URL record given url and baseURL.\n    const urlRecord = getURLRecord(url, baseURL)\n\n    // 3. If protocols is a string, set protocols to a sequence consisting\n    //    of just that string.\n    if (typeof protocols === 'string') {\n      protocols = [protocols]\n    }\n\n    // 4. If any of the values in protocols occur more than once or otherwise\n    //    fail to match the requirements for elements that comprise the value\n    //    of `Sec-WebSocket-Protocol` fields as defined by The WebSocket\n    //    protocol, then throw a \"SyntaxError\" DOMException.\n    if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {\n      throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n    }\n\n    if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {\n      throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n    }\n\n    // 5. Set this's url to urlRecord.\n    this.#url = new URL(urlRecord.href)\n\n    // 6. Let client be this's relevant settings object.\n    const client = environmentSettingsObject.settingsObject\n\n    // 7. Run this step in parallel:\n    // 7.1. Establish a WebSocket connection given urlRecord, protocols,\n    //      and client.\n    this.#handler.controller = establishWebSocketConnection(\n      urlRecord,\n      protocols,\n      client,\n      this.#handler,\n      options\n    )\n\n    // Each WebSocket object has an associated ready state, which is a\n    // number representing the state of the connection. Initially it must\n    // be CONNECTING (0).\n    this.#handler.readyState = WebSocket.CONNECTING\n\n    // The extensions attribute must initially return the empty string.\n\n    // The protocol attribute must initially return the empty string.\n\n    // Each WebSocket object has an associated binary type, which is a\n    // BinaryType. Initially it must be \"blob\".\n    this.#binaryType = 'blob'\n  }\n\n  /**\n   * @see https://websockets.spec.whatwg.org/#dom-websocket-close\n   * @param {number|undefined} code\n   * @param {string|undefined} reason\n   */\n  close (code = undefined, reason = undefined) {\n    webidl.brandCheck(this, WebSocket)\n\n    const prefix = 'WebSocket.close'\n\n    if (code !== undefined) {\n      code = webidl.converters['unsigned short'](code, prefix, 'code', webidl.attributes.Clamp)\n    }\n\n    if (reason !== undefined) {\n      reason = webidl.converters.USVString(reason)\n    }\n\n    // 1. If code is the special value \"missing\", then set code to null.\n    code ??= null\n\n    // 2. If reason is the special value \"missing\", then set reason to the empty string.\n    reason ??= ''\n\n    // 3. Close the WebSocket with this, code, and reason.\n    closeWebSocketConnection(this.#handler, code, reason, true)\n  }\n\n  /**\n   * @see https://websockets.spec.whatwg.org/#dom-websocket-send\n   * @param {NodeJS.TypedArray|ArrayBuffer|Blob|string} data\n   */\n  send (data) {\n    webidl.brandCheck(this, WebSocket)\n\n    const prefix = 'WebSocket.send'\n    webidl.argumentLengthCheck(arguments, 1, prefix)\n\n    data = webidl.converters.WebSocketSendData(data, prefix, 'data')\n\n    // 1. If this's ready state is CONNECTING, then throw an\n    //    \"InvalidStateError\" DOMException.\n    if (isConnecting(this.#handler.readyState)) {\n      throw new DOMException('Sent before connected.', 'InvalidStateError')\n    }\n\n    // 2. Run the appropriate set of steps from the following list:\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-6.1\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2\n\n    if (!isEstablished(this.#handler.readyState) || isClosing(this.#handler.readyState)) {\n      return\n    }\n\n    // If data is a string\n    if (typeof data === 'string') {\n      // If the WebSocket connection is established and the WebSocket\n      // closing handshake has not yet started, then the user agent\n      // must send a WebSocket Message comprised of the data argument\n      // using a text frame opcode; if the data cannot be sent, e.g.\n      // because it would need to be buffered but the buffer is full,\n      // the user agent must flag the WebSocket as full and then close\n      // the WebSocket connection. Any invocation of this method with a\n      // string argument that does not throw an exception must increase\n      // the bufferedAmount attribute by the number of bytes needed to\n      // express the argument as UTF-8.\n\n      const buffer = Buffer.from(data)\n\n      this.#bufferedAmount += buffer.byteLength\n      this.#sendQueue.add(buffer, () => {\n        this.#bufferedAmount -= buffer.byteLength\n      }, sendHints.text)\n    } else if (isArrayBuffer(data)) {\n      // If the WebSocket connection is established, and the WebSocket\n      // closing handshake has not yet started, then the user agent must\n      // send a WebSocket Message comprised of data using a binary frame\n      // opcode; if the data cannot be sent, e.g. because it would need\n      // to be buffered but the buffer is full, the user agent must flag\n      // the WebSocket as full and then close the WebSocket connection.\n      // The data to be sent is the data stored in the buffer described\n      // by the ArrayBuffer object. Any invocation of this method with an\n      // ArrayBuffer argument that does not throw an exception must\n      // increase the bufferedAmount attribute by the length of the\n      // ArrayBuffer in bytes.\n\n      this.#bufferedAmount += data.byteLength\n      this.#sendQueue.add(data, () => {\n        this.#bufferedAmount -= data.byteLength\n      }, sendHints.arrayBuffer)\n    } else if (ArrayBuffer.isView(data)) {\n      // If the WebSocket connection is established, and the WebSocket\n      // closing handshake has not yet started, then the user agent must\n      // send a WebSocket Message comprised of data using a binary frame\n      // opcode; if the data cannot be sent, e.g. because it would need to\n      // be buffered but the buffer is full, the user agent must flag the\n      // WebSocket as full and then close the WebSocket connection. The\n      // data to be sent is the data stored in the section of the buffer\n      // described by the ArrayBuffer object that data references. Any\n      // invocation of this method with this kind of argument that does\n      // not throw an exception must increase the bufferedAmount attribute\n      // by the length of data’s buffer in bytes.\n\n      this.#bufferedAmount += data.byteLength\n      this.#sendQueue.add(data, () => {\n        this.#bufferedAmount -= data.byteLength\n      }, sendHints.typedArray)\n    } else if (webidl.is.Blob(data)) {\n      // If the WebSocket connection is established, and the WebSocket\n      // closing handshake has not yet started, then the user agent must\n      // send a WebSocket Message comprised of data using a binary frame\n      // opcode; if the data cannot be sent, e.g. because it would need to\n      // be buffered but the buffer is full, the user agent must flag the\n      // WebSocket as full and then close the WebSocket connection. The data\n      // to be sent is the raw data represented by the Blob object. Any\n      // invocation of this method with a Blob argument that does not throw\n      // an exception must increase the bufferedAmount attribute by the size\n      // of the Blob object’s raw data, in bytes.\n\n      this.#bufferedAmount += data.size\n      this.#sendQueue.add(data, () => {\n        this.#bufferedAmount -= data.size\n      }, sendHints.blob)\n    }\n  }\n\n  get readyState () {\n    webidl.brandCheck(this, WebSocket)\n\n    // The readyState getter steps are to return this's ready state.\n    return this.#handler.readyState\n  }\n\n  get bufferedAmount () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#bufferedAmount\n  }\n\n  get url () {\n    webidl.brandCheck(this, WebSocket)\n\n    // The url getter steps are to return this's url, serialized.\n    return URLSerializer(this.#url)\n  }\n\n  get extensions () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#extensions\n  }\n\n  get protocol () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#protocol\n  }\n\n  get onopen () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#events.open\n  }\n\n  set onopen (fn) {\n    webidl.brandCheck(this, WebSocket)\n\n    if (this.#events.open) {\n      this.removeEventListener('open', this.#events.open)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('open', listener)\n      this.#events.open = fn\n    } else {\n      this.#events.open = null\n    }\n  }\n\n  get onerror () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#events.error\n  }\n\n  set onerror (fn) {\n    webidl.brandCheck(this, WebSocket)\n\n    if (this.#events.error) {\n      this.removeEventListener('error', this.#events.error)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('error', listener)\n      this.#events.error = fn\n    } else {\n      this.#events.error = null\n    }\n  }\n\n  get onclose () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#events.close\n  }\n\n  set onclose (fn) {\n    webidl.brandCheck(this, WebSocket)\n\n    if (this.#events.close) {\n      this.removeEventListener('close', this.#events.close)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('close', listener)\n      this.#events.close = fn\n    } else {\n      this.#events.close = null\n    }\n  }\n\n  get onmessage () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#events.message\n  }\n\n  set onmessage (fn) {\n    webidl.brandCheck(this, WebSocket)\n\n    if (this.#events.message) {\n      this.removeEventListener('message', this.#events.message)\n    }\n\n    const listener = webidl.converters.EventHandlerNonNull(fn)\n\n    if (listener !== null) {\n      this.addEventListener('message', listener)\n      this.#events.message = fn\n    } else {\n      this.#events.message = null\n    }\n  }\n\n  get binaryType () {\n    webidl.brandCheck(this, WebSocket)\n\n    return this.#binaryType\n  }\n\n  set binaryType (type) {\n    webidl.brandCheck(this, WebSocket)\n\n    if (type !== 'blob' && type !== 'arraybuffer') {\n      this.#binaryType = 'blob'\n    } else {\n      this.#binaryType = type\n    }\n  }\n\n  /**\n   * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol\n   */\n  #onConnectionEstablished (response, parsedExtensions) {\n    // processResponse is called when the \"response's header list has been received and initialized.\"\n    // once this happens, the connection is open\n    this.#handler.socket = response.socket\n\n    const parser = new ByteParser(this.#handler, parsedExtensions)\n    parser.on('drain', () => this.#handler.onParserDrain())\n    parser.on('error', (err) => this.#handler.onParserError(err))\n\n    this.#parser = parser\n    this.#sendQueue = new SendQueue(response.socket)\n\n    // 1. Change the ready state to OPEN (1).\n    this.#handler.readyState = states.OPEN\n\n    // 2. Change the extensions attribute’s value to the extensions in use, if\n    //    it is not the null value.\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1\n    const extensions = response.headersList.get('sec-websocket-extensions')\n\n    if (extensions !== null) {\n      this.#extensions = extensions\n    }\n\n    // 3. Change the protocol attribute’s value to the subprotocol in use, if\n    //    it is not the null value.\n    // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9\n    const protocol = response.headersList.get('sec-websocket-protocol')\n\n    if (protocol !== null) {\n      this.#protocol = protocol\n    }\n\n    // 4. Fire an event named open at the WebSocket object.\n    fireEvent('open', this)\n\n    if (channels.open.hasSubscribers) {\n      // Convert headers to a plain object for the event\n      const headers = response.headersList.entries\n      channels.open.publish({\n        address: response.socket.address(),\n        protocol: this.#protocol,\n        extensions: this.#extensions,\n        websocket: this,\n        handshakeResponse: {\n          status: response.status,\n          statusText: response.statusText,\n          headers\n        }\n      })\n    }\n  }\n\n  #onMessage (type, data) {\n    // 1. If ready state is not OPEN (1), then return.\n    if (this.#handler.readyState !== states.OPEN) {\n      return\n    }\n\n    // 2. Let dataForEvent be determined by switching on type and binary type:\n    let dataForEvent\n\n    if (type === opcodes.TEXT) {\n      // -> type indicates that the data is Text\n      //      a new DOMString containing data\n      try {\n        dataForEvent = utf8Decode(data)\n      } catch {\n        failWebsocketConnection(this.#handler, 1007, 'Received invalid UTF-8 in text frame.')\n        return\n      }\n    } else if (type === opcodes.BINARY) {\n      if (this.#binaryType === 'blob') {\n        // -> type indicates that the data is Binary and binary type is \"blob\"\n        //      a new Blob object, created in the relevant Realm of the WebSocket\n        //      object, that represents data as its raw data\n        dataForEvent = new Blob([data])\n      } else {\n        // -> type indicates that the data is Binary and binary type is \"arraybuffer\"\n        //      a new ArrayBuffer object, created in the relevant Realm of the\n        //      WebSocket object, whose contents are data\n        dataForEvent = toArrayBuffer(data)\n      }\n    }\n\n    // 3. Fire an event named message at the WebSocket object, using MessageEvent,\n    //    with the origin attribute initialized to the serialization of the WebSocket\n    //    object’s url's origin, and the data attribute initialized to dataForEvent.\n    fireEvent('message', this, createFastMessageEvent, {\n      origin: this.#url.origin,\n      data: dataForEvent\n    })\n  }\n\n  #onParserDrain () {\n    this.#handler.socket.resume()\n  }\n\n  /**\n   * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol\n   * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4\n   */\n  #onSocketClose () {\n    // If the TCP connection was closed after the\n    // WebSocket closing handshake was completed, the WebSocket connection\n    // is said to have been closed _cleanly_.\n    const wasClean =\n      this.#handler.closeState.has(sentCloseFrameState.SENT) &&\n      this.#handler.closeState.has(sentCloseFrameState.RECEIVED)\n\n    let code = 1005\n    let reason = ''\n\n    const result = this.#parser?.closingInfo\n\n    if (result && !result.error) {\n      code = result.code ?? 1005\n      reason = result.reason\n    }\n\n    // 1. Change the ready state to CLOSED (3).\n    this.#handler.readyState = states.CLOSED\n\n    // 2. If the user agent was required to fail the WebSocket\n    //    connection, or if the WebSocket connection was closed\n    //    after being flagged as full, fire an event named error\n    //    at the WebSocket object.\n    if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {\n      // If _The WebSocket\n      // Connection is Closed_ and no Close control frame was received by the\n      // endpoint (such as could occur if the underlying transport connection\n      // is lost), _The WebSocket Connection Close Code_ is considered to be\n      // 1006.\n      code = 1006\n\n      fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {\n        error: new TypeError(reason)\n      })\n    }\n\n    // 3. Fire an event named close at the WebSocket object,\n    //    using CloseEvent, with the wasClean attribute\n    //    initialized to true if the connection closed cleanly\n    //    and false otherwise, the code attribute initialized to\n    //    the WebSocket connection close code, and the reason\n    //    attribute initialized to the result of applying UTF-8\n    //    decode without BOM to the WebSocket connection close\n    //    reason.\n    // TODO: process.nextTick\n    fireEvent('close', this, (type, init) => new CloseEvent(type, init), {\n      wasClean, code, reason\n    })\n\n    if (channels.close.hasSubscribers) {\n      channels.close.publish({\n        websocket: this,\n        code,\n        reason\n      })\n    }\n  }\n\n  /**\n   * @param {WebSocket} ws\n   * @param {Buffer|undefined} buffer\n   */\n  static ping (ws, buffer) {\n    if (Buffer.isBuffer(buffer)) {\n      if (buffer.length > 125) {\n        throw new TypeError('A PING frame cannot have a body larger than 125 bytes.')\n      }\n    } else if (buffer !== undefined) {\n      throw new TypeError('Expected buffer payload')\n    }\n\n    // An endpoint MAY send a Ping frame any time after the connection is\n    // established and before the connection is closed.\n    const readyState = ws.#handler.readyState\n\n    if (isEstablished(readyState) && !isClosing(readyState) && !isClosed(readyState)) {\n      const frame = new WebsocketFrameSend(buffer)\n      ws.#handler.socket.write(frame.createFrame(opcodes.PING))\n    }\n  }\n}\n\nconst { ping } = WebSocket\nReflect.deleteProperty(WebSocket, 'ping')\n\n// https://websockets.spec.whatwg.org/#dom-websocket-connecting\nWebSocket.CONNECTING = WebSocket.prototype.CONNECTING = states.CONNECTING\n// https://websockets.spec.whatwg.org/#dom-websocket-open\nWebSocket.OPEN = WebSocket.prototype.OPEN = states.OPEN\n// https://websockets.spec.whatwg.org/#dom-websocket-closing\nWebSocket.CLOSING = WebSocket.prototype.CLOSING = states.CLOSING\n// https://websockets.spec.whatwg.org/#dom-websocket-closed\nWebSocket.CLOSED = WebSocket.prototype.CLOSED = states.CLOSED\n\nObject.defineProperties(WebSocket.prototype, {\n  CONNECTING: staticPropertyDescriptors,\n  OPEN: staticPropertyDescriptors,\n  CLOSING: staticPropertyDescriptors,\n  CLOSED: staticPropertyDescriptors,\n  url: kEnumerableProperty,\n  readyState: kEnumerableProperty,\n  bufferedAmount: kEnumerableProperty,\n  onopen: kEnumerableProperty,\n  onerror: kEnumerableProperty,\n  onclose: kEnumerableProperty,\n  close: kEnumerableProperty,\n  onmessage: kEnumerableProperty,\n  binaryType: kEnumerableProperty,\n  send: kEnumerableProperty,\n  extensions: kEnumerableProperty,\n  protocol: kEnumerableProperty,\n  [Symbol.toStringTag]: {\n    value: 'WebSocket',\n    writable: false,\n    enumerable: false,\n    configurable: true\n  }\n})\n\nObject.defineProperties(WebSocket, {\n  CONNECTING: staticPropertyDescriptors,\n  OPEN: staticPropertyDescriptors,\n  CLOSING: staticPropertyDescriptors,\n  CLOSED: staticPropertyDescriptors\n})\n\nwebidl.converters['sequence<DOMString>'] = webidl.sequenceConverter(\n  webidl.converters.DOMString\n)\n\nwebidl.converters['DOMString or sequence<DOMString>'] = function (V, prefix, argument) {\n  if (webidl.util.Type(V) === webidl.util.Types.OBJECT && Symbol.iterator in V) {\n    return webidl.converters['sequence<DOMString>'](V)\n  }\n\n  return webidl.converters.DOMString(V, prefix, argument)\n}\n\n// This implements the proposal made in https://github.com/whatwg/websockets/issues/42\nwebidl.converters.WebSocketInit = webidl.dictionaryConverter([\n  {\n    key: 'protocols',\n    converter: webidl.converters['DOMString or sequence<DOMString>'],\n    defaultValue: () => []\n  },\n  {\n    key: 'dispatcher',\n    converter: webidl.converters.any,\n    defaultValue: () => getGlobalDispatcher()\n  },\n  {\n    key: 'headers',\n    converter: webidl.nullableConverter(webidl.converters.HeadersInit)\n  }\n])\n\nwebidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = function (V) {\n  if (webidl.util.Type(V) === webidl.util.Types.OBJECT && !(Symbol.iterator in V)) {\n    return webidl.converters.WebSocketInit(V)\n  }\n\n  return { protocols: webidl.converters['DOMString or sequence<DOMString>'](V) }\n}\n\nwebidl.converters.WebSocketSendData = function (V) {\n  if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {\n    if (webidl.is.Blob(V)) {\n      return V\n    }\n\n    if (webidl.is.BufferSource(V)) {\n      return V\n    }\n  }\n\n  return webidl.converters.USVString(V)\n}\n\nmodule.exports = {\n  WebSocket,\n  ping\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"undici\",\n  \"version\": \"7.24.5\",\n  \"description\": \"An HTTP/1.1 client, written from scratch for Node.js\",\n  \"homepage\": \"https://undici.nodejs.org\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nodejs/undici/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/nodejs/undici.git\"\n  },\n  \"license\": \"MIT\",\n  \"contributors\": [\n    {\n      \"name\": \"Daniele Belardi\",\n      \"url\": \"https://github.com/dnlup\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Ethan Arrowood\",\n      \"url\": \"https://github.com/ethan-arrowood\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Matteo Collina\",\n      \"url\": \"https://github.com/mcollina\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Matthew Aitken\",\n      \"url\": \"https://github.com/KhafraDev\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Robert Nagy\",\n      \"url\": \"https://github.com/ronag\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Szymon Marczak\",\n      \"url\": \"https://github.com/szmarczak\",\n      \"author\": true\n    },\n    {\n      \"name\": \"Tomas Della Vedova\",\n      \"url\": \"https://github.com/delvedor\",\n      \"author\": true\n    }\n  ],\n  \"keywords\": [\n    \"fetch\",\n    \"http\",\n    \"https\",\n    \"promise\",\n    \"request\",\n    \"curl\",\n    \"wget\",\n    \"xhr\",\n    \"whatwg\"\n  ],\n  \"main\": \"index.js\",\n  \"types\": \"index.d.ts\",\n  \"scripts\": {\n    \"build:node\": \"esbuild index-fetch.js --bundle --platform=node --outfile=undici-fetch.js --define:esbuildDetection=1 --keep-names && node scripts/strip-comments.js\",\n    \"build:wasm\": \"node build/wasm.js --docker\",\n    \"generate-pem\": \"node scripts/generate-pem.js\",\n    \"lint\": \"eslint --cache\",\n    \"lint:fix\": \"eslint --fix --cache\",\n    \"test\": \"npm run test:javascript && cross-env NODE_V8_COVERAGE= npm run test:typescript\",\n    \"test:javascript\": \"npm run test:javascript:no-jest && npm run test:jest\",\n    \"test:javascript:no-jest\": \"npm run generate-pem && npm run test:unit && npm run test:fetch && npm run test:node-fetch && npm run test:infra && npm run test:cache && npm run test:cache-interceptor && npm run test:interceptors && npm run test:cookies && npm run test:eventsource && npm run test:subresource-integrity && npm run test:wpt && npm run test:websocket && npm run test:node-test && npm run test:cache-tests\",\n    \"test:javascript:without-intl\": \"npm run test:javascript:no-jest\",\n    \"test:busboy\": \"borp --timeout 180000 -p \\\"test/busboy/*.js\\\"\",\n    \"test:cache\": \"borp --timeout 180000 -p \\\"test/cache/*.js\\\"\",\n    \"test:cache-interceptor\": \"borp --timeout 180000 -p \\\"test/cache-interceptor/*.js\\\"\",\n    \"test:cache-interceptor:sqlite\": \"cross-env NODE_OPTIONS=--experimental-sqlite npm run test:cache-interceptor\",\n    \"test:cookies\": \"borp --timeout 180000 -p \\\"test/cookie/*.js\\\"\",\n    \"test:eventsource\": \"npm run build:node && borp --timeout 180000 --expose-gc -p \\\"test/eventsource/*.js\\\"\",\n    \"test:fuzzing\": \"node test/fuzzing/fuzzing.test.js\",\n    \"test:fetch\": \"npm run build:node && borp --timeout 180000 --expose-gc --concurrency 1 -p \\\"test/fetch/*.js\\\" && npm run test:webidl && npm run test:busboy\",\n    \"test:subresource-integrity\": \"borp --timeout 180000 -p \\\"test/subresource-integrity/*.js\\\"\",\n    \"test:h2\": \"npm run test:h2:core && npm run test:h2:fetch\",\n    \"test:h2:core\": \"borp --timeout 180000 -p \\\"test/+(http2|h2)*.js\\\"\",\n    \"test:h2:fetch\": \"npm run build:node && borp --timeout 180000 -p \\\"test/fetch/http2*.js\\\"\",\n    \"test:infra\": \"borp --timeout 180000 -p \\\"test/infra/*.js\\\"\",\n    \"test:interceptors\": \"borp --timeout 180000 -p \\\"test/interceptors/*.js\\\"\",\n    \"test:jest\": \"cross-env NODE_V8_COVERAGE= jest\",\n    \"test:unit\": \"borp --timeout 180000 --expose-gc -p \\\"test/*.js\\\"\",\n    \"test:node-fetch\": \"borp --timeout 180000 -p \\\"test/node-fetch/**/*.js\\\"\",\n    \"test:node-test\": \"borp --timeout 180000 -p \\\"test/node-test/**/*.js\\\"\",\n    \"test:tdd\": \"borp --timeout 180000 --expose-gc -p \\\"test/*.js\\\"\",\n    \"test:tdd:node-test\": \"borp --timeout 180000 -p \\\"test/node-test/**/*.js\\\" -w\",\n    \"test:typescript\": \"tsd && tsc test/imports/undici-import.ts --typeRoots ./types --noEmit && tsc ./types/*.d.ts --noEmit --typeRoots ./types\",\n    \"test:webidl\": \"borp --timeout 180000 -p \\\"test/webidl/*.js\\\"\",\n    \"test:websocket\": \"borp --timeout 180000 -p \\\"test/websocket/**/*.js\\\"\",\n    \"test:websocket:autobahn\": \"node test/autobahn/client.js\",\n    \"test:websocket:autobahn:report\": \"node test/autobahn/report.js\",\n    \"test:wpt:setup\": \"node test/web-platform-tests/wpt-runner.mjs setup\",\n    \"test:wpt\": \"npm run test:wpt:setup && node test/web-platform-tests/wpt-runner.mjs run /fetch /mimesniff /xhr /websockets /serviceWorkers /eventsource\",\n    \"test:cache-tests\": \"node test/cache-interceptor/cache-tests.mjs --ci\",\n    \"coverage\": \"npm run coverage:clean && cross-env NODE_V8_COVERAGE=./coverage/tmp npm run test:javascript && npm run coverage:report\",\n    \"coverage:ci\": \"npm run coverage:clean && cross-env NODE_V8_COVERAGE=./coverage/tmp npm run test:javascript && npm run coverage:report:ci\",\n    \"coverage:clean\": \"node ./scripts/clean-coverage.js\",\n    \"coverage:report\": \"cross-env NODE_V8_COVERAGE= c8 report\",\n    \"coverage:report:ci\": \"c8 report\",\n    \"bench\": \"echo \\\"Error: Benchmarks have been moved to '/benchmarks'\\\" && exit 1\",\n    \"serve:website\": \"echo \\\"Error: Documentation has been moved to '/docs'\\\" && exit 1\",\n    \"prepare\": \"husky && node ./scripts/platform-shell.js\"\n  },\n  \"devDependencies\": {\n    \"@fastify/busboy\": \"3.2.0\",\n    \"@matteo.collina/tspl\": \"^0.2.0\",\n    \"@metcoder95/https-pem\": \"^1.0.0\",\n    \"@sinonjs/fake-timers\": \"^12.0.0\",\n    \"@types/node\": \"^20.19.22\",\n    \"abort-controller\": \"^3.0.0\",\n    \"borp\": \"^0.20.0\",\n    \"c8\": \"^10.0.0\",\n    \"cross-env\": \"^10.0.0\",\n    \"dns-packet\": \"^5.4.0\",\n    \"esbuild\": \"^0.27.3\",\n    \"eslint\": \"^9.9.0\",\n    \"fast-check\": \"^4.1.1\",\n    \"husky\": \"^9.0.7\",\n    \"jest\": \"^30.0.5\",\n    \"jsondiffpatch\": \"^0.7.3\",\n    \"neostandard\": \"^0.12.0\",\n    \"node-forge\": \"^1.3.1\",\n    \"proxy\": \"^2.1.1\",\n    \"tsd\": \"^0.33.0\",\n    \"typescript\": \"^5.6.2\",\n    \"ws\": \"^8.11.0\"\n  },\n  \"engines\": {\n    \"node\": \">=20.18.1\"\n  },\n  \"tsd\": {\n    \"directory\": \"test/types\",\n    \"compilerOptions\": {\n      \"esModuleInterop\": true,\n      \"lib\": [\n        \"esnext\"\n      ]\n    }\n  },\n  \"jest\": {\n    \"testMatch\": [\n      \"<rootDir>/test/jest/**\"\n    ]\n  }\n}\n"
  },
  {
    "path": "scripts/clean-coverage.js",
    "content": "'use strict'\n\nconst { rmSync } = require('node:fs')\nconst { resolve } = require('node:path')\n\nif (process.env.NODE_V8_COVERAGE) {\n  if (process.env.NODE_V8_COVERAGE.endsWith('/tmp')) {\n    rmSync(resolve(__dirname, process.env.NODE_V8_COVERAGE, '..'), { recursive: true, force: true })\n  } else {\n    rmSync(resolve(__dirname, process.env.NODE_V8_COVERAGE), { recursive: true, force: true })\n  }\n} else {\n  console.log(resolve(__dirname, 'coverage'))\n  rmSync(resolve(__dirname, '../coverage'), { recursive: true, force: true })\n}\n"
  },
  {
    "path": "scripts/generate-pem.js",
    "content": "'use strict'\n\nrequire('@metcoder95/https-pem/install')\n"
  },
  {
    "path": "scripts/generate-undici-types-package-json.js",
    "content": "'use strict'\n\nconst fs = require('node:fs')\nconst path = require('node:path')\n\nconst packageJSONPath = path.join(__dirname, '..', 'package.json')\nconst packageJSONRaw = fs.readFileSync(packageJSONPath, 'utf-8')\nconst packageJSON = JSON.parse(packageJSONRaw)\n\nconst licensePath = path.join(__dirname, '..', 'LICENSE')\nconst licenseRaw = fs.readFileSync(licensePath, 'utf-8')\n\nconst packageTypesJSON = {\n  name: 'undici-types',\n  version: packageJSON.version,\n  description: 'A stand-alone types package for Undici',\n  homepage: packageJSON.homepage,\n  bugs: packageJSON.bugs,\n  repository: packageJSON.repository,\n  license: packageJSON.license,\n  types: 'index.d.ts',\n  files: ['*.d.ts'],\n  contributors: packageJSON.contributors\n}\n\nconst packageTypesPath = path.join(__dirname, '..', 'types', 'package.json')\nconst licenseTypesPath = path.join(__dirname, '..', 'types', 'LICENSE')\n\nfs.writeFileSync(packageTypesPath, JSON.stringify(packageTypesJSON, null, 2))\nfs.writeFileSync(licenseTypesPath, licenseRaw)\n"
  },
  {
    "path": "scripts/platform-shell.js",
    "content": "'use strict'\n\nconst { platform } = require('node:os')\nconst { writeFileSync } = require('node:fs')\nconst { resolve } = require('node:path')\n\nif (platform() === 'win32') {\n  writeFileSync(\n    resolve(__dirname, '.npmrc'),\n    'script-shell = \"C:\\\\windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\"\\n'\n  )\n}\n"
  },
  {
    "path": "scripts/release.js",
    "content": "'use strict'\n\n// Called from .github/workflows\n\nconst generateReleaseNotes = async ({ github, owner, repo, versionTag, commitHash }) => {\n  const { data: releases } = await github.rest.repos.listReleases({\n    owner,\n    repo\n  })\n\n  const previousRelease = releases.find((r) => r.tag_name.startsWith('v7'))\n\n  const { data: { body } } = await github.rest.repos.generateReleaseNotes({\n    owner,\n    repo,\n    tag_name: versionTag,\n    target_commitish: commitHash,\n    previous_tag_name: previousRelease?.tag_name\n  })\n\n  const bodyWithoutReleasePr = body.split('\\n')\n    .filter((line) => !line.includes('[Release] v'))\n    .join('\\n')\n\n  return bodyWithoutReleasePr\n}\n\nconst generatePr = async ({ github, context, defaultBranch, versionTag, commitHash }) => {\n  const { owner, repo } = context.repo\n  const releaseNotes = await generateReleaseNotes({ github, owner, repo, versionTag, commitHash })\n\n  await github.rest.pulls.create({\n    owner,\n    repo,\n    head: `release/${versionTag}`,\n    base: defaultBranch,\n    title: `[Release] ${versionTag}`,\n    body: releaseNotes\n  })\n}\n\nconst release = async ({ github, context, versionTag, commitHash }) => {\n  const { owner, repo } = context.repo\n  const releaseNotes = await generateReleaseNotes({ github, owner, repo, versionTag, commitHash })\n\n  await github.rest.repos.createRelease({\n    owner,\n    repo,\n    tag_name: versionTag,\n    target_commitish: commitHash,\n    name: versionTag,\n    body: releaseNotes,\n    draft: false,\n    prerelease: false,\n    generate_release_notes: false\n  })\n\n  try {\n    await github.rest.git.deleteRef({\n      owner,\n      repo,\n      ref: `heads/release/${versionTag}`\n    })\n  } catch (err) {\n    console.log(\"Couldn't delete release PR ref\")\n    console.log(err)\n  }\n}\n\nmodule.exports = {\n  generatePr,\n  release\n}\n"
  },
  {
    "path": "scripts/strip-comments.js",
    "content": "'use strict'\n\nconst { readFileSync, writeFileSync } = require('node:fs')\nconst { transcode } = require('node:buffer')\n\nconst buffer = transcode\n  ? transcode(readFileSync('./undici-fetch.js'), 'utf8', 'latin1')\n  : readFileSync('./undici-fetch.js')\n\nwriteFileSync('./undici-fetch.js', buffer.toString('latin1'))\n"
  },
  {
    "path": "test/autobahn/.gitignore",
    "content": "reports/clients\n"
  },
  {
    "path": "test/autobahn/client.js",
    "content": "'use strict'\n\nconst { WebSocket } = require('../..')\n\nconst logOnError = process.env.LOG_ON_ERROR === 'true'\n\nlet currentTest = 1\nlet testCount\n\nconst autobahnFuzzingserverUrl = process.env.FUZZING_SERVER_URL || 'ws://localhost:9001'\n\nfunction nextTest () {\n  let ws\n\n  if (currentTest > testCount) {\n    ws = new WebSocket(`${autobahnFuzzingserverUrl}/updateReports?agent=undici`)\n    ws.addEventListener('close', () => require('./report'))\n    return\n  }\n\n  console.log(`Running test case ${currentTest}/${testCount}`)\n\n  ws = new WebSocket(\n    `${autobahnFuzzingserverUrl}/runCase?case=${currentTest}&agent=undici`\n  )\n  ws.addEventListener('message', (data) => {\n    ws.send(data.data)\n  })\n  ws.addEventListener('close', () => {\n    currentTest++\n    process.nextTick(nextTest)\n  })\n  if (logOnError) {\n    ws.addEventListener('error', (e) => {\n      console.error(e.error)\n    })\n  }\n}\n\nconst ws = new WebSocket(`${autobahnFuzzingserverUrl}/getCaseCount`)\nws.addEventListener('message', (data) => {\n  testCount = parseInt(data.data)\n})\nws.addEventListener('close', () => {\n  if (testCount > 0) {\n    nextTest()\n  }\n})\nws.addEventListener('error', (e) => {\n  console.error(e.error)\n  process.exitCode = 1\n})\n"
  },
  {
    "path": "test/autobahn/config/fuzzingserver.json",
    "content": "{\n  \"url\": \"ws://127.0.0.1:9001\",\n  \"outdir\": \"./reports/clients\",\n  \"cases\": [\"*\"],\n  \"exclude-cases\": [],\n  \"exclude-agent-cases\": {}\n}\n"
  },
  {
    "path": "test/autobahn/report.js",
    "content": "'use strict'\n\nconst result = require('./reports/clients/index.json').undici\n\nconst failOnError = process.env.FAIL_ON_ERROR === 'true'\nlet runFailed = false\n\nlet okTests = 0\nlet failedTests = 0\nlet nonStrictTests = 0\nlet wrongCodeTests = 0\nlet uncleanTests = 0\nlet failedByClientTests = 0\nlet informationalTests = 0\nlet unimplementedTests = 0\n\nlet totalTests = 0\n\nfunction testCaseIdToWeight (testCaseId) {\n  const [major, minor, sub] = testCaseId.split('.')\n  return sub\n    ? parseInt(major, 10) * 10000 + parseInt(minor, 10) * 100 + parseInt(sub, 10)\n    : parseInt(major, 10) * 10000 + parseInt(minor, 10) * 100\n}\n\nfunction isFailedTestCase (testCase) {\n  return (\n    testCase.behavior === 'FAILED' ||\n    testCase.behavior === 'WRONG CODE' ||\n    testCase.behavior === 'UNCLEAN' ||\n    testCase.behavior === 'FAILED BY CLIENT' ||\n    testCase.behaviorClose === 'FAILED' ||\n    testCase.behaviorClose === 'WRONG CODE' ||\n    testCase.behaviorClose === 'UNCLEAN' ||\n    testCase.behaviorClose === 'FAILED BY CLIENT'\n  )\n}\n\nconst keys = Object.keys(result).sort((a, b) => {\n  a = testCaseIdToWeight(a)\n  b = testCaseIdToWeight(b)\n  return a - b\n})\n\nconst reorderedResult = {}\nfor (const key of keys) {\n  reorderedResult[key] = result[key]\n  delete reorderedResult[key].reportfile\n\n  totalTests++\n\n  if (\n    failOnError &&\n    !runFailed &&\n    isFailedTestCase(result[key])\n  ) {\n    runFailed = true\n  }\n\n  switch (result[key].behavior) {\n    case 'OK':\n      okTests++\n      break\n    case 'FAILED':\n      failedTests++\n      break\n    case 'NON-STRICT':\n      nonStrictTests++\n      break\n    case 'WRONG CODE':\n      wrongCodeTests++\n      break\n    case 'UNCLEAN':\n      uncleanTests++\n      break\n    case 'FAILED BY CLIENT':\n      failedByClientTests++\n      break\n    case 'INFORMATIONAL':\n      informationalTests++\n      break\n    case 'UNIMPLEMENTED':\n      unimplementedTests++\n      break\n  }\n}\n\nconsole.log('Autobahn Test Report\\n\\nSummary:')\n\nconsole.table({\n  OK: okTests,\n  Failed: failedTests,\n  'Non-Strict': nonStrictTests,\n  'Wrong Code': wrongCodeTests,\n  Unclean: uncleanTests,\n  'Failed By Client': failedByClientTests,\n  Informational: informationalTests,\n  Unimplemented: unimplementedTests,\n  Total: totalTests\n})\n\nconsole.log('Details:')\n\nconsole.table(reorderedResult)\n\nprocess.exitCode = runFailed ? 1 : 0\n"
  },
  {
    "path": "test/autobahn/run.sh",
    "content": "docker run -it --rm \\\n    -v \"${PWD}/config:/config\" \\\n    -v \"${PWD}/reports:/reports\" \\\n    -p 9001:9001 \\\n    --name fuzzingserver \\\n    crossbario/autobahn-testsuite\n"
  },
  {
    "path": "test/busboy/LICENSE",
    "content": "Copyright Brian White. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n"
  },
  {
    "path": "test/busboy/formdata-test.js",
    "content": "'use strict'\n\n// Copyright 2009 The Go Authors.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//    * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//    * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//    * Neither the name of Google LLC nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// https://github.com/golang/go/blob/16e5d24480dca7ddcbdffb78a8ed5de3e5155dec/src/mime/multipart/formdata_test.go\n\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\nconst fileaContents = 'This is a test file.'\nconst filebContents = 'Another test file.'\nconst textaValue = 'foo'\nconst textbValue = 'bar'\nconst boundary = 'MyBoundary'\n\nconst message = [\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"filea\"; filename=\"filea.txt\"',\n  'Content-Type: text/plain',\n  '',\n  fileaContents,\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"fileb\"; filename=\"fileb.txt\"',\n  'Content-Type: text/plain',\n  '',\n  filebContents,\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"texta\"',\n  '',\n  textaValue,\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"textb\"',\n  '',\n  textbValue,\n  '--MyBoundary--'\n].join('\\r\\n')\n\nconst messageWithFileWithoutName = [\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"hiddenfile\"; filename=\"\"',\n  'Content-Type: text/plain',\n  '',\n  filebContents,\n  '--MyBoundary--'\n].join('\\r\\n')\n\nconst messageWithFileName = [\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"filea\"; filename=\"filea.txt\"',\n  'Content-Type: text/plain',\n  '',\n  fileaContents,\n  '--MyBoundary--'\n].join('\\r\\n')\n\nconst messageWithTextContentType = [\n  '--MyBoundary',\n  'Content-Disposition: form-data; name=\"texta\"',\n  'Content-Type: text/plain',\n  '',\n  textaValue,\n  '--MyBoundary--'\n].join('\\r\\n')\n\nfunction makeResponse (body, bnd) {\n  return new Response(body, {\n    headers: {\n      'content-type': `multipart/form-data; boundary=${bnd}`\n    }\n  })\n}\n\ntest('ReadForm - fields and files', async (t) => {\n  const response = makeResponse(message, boundary)\n  const fd = await response.formData()\n\n  t.assert.strictEqual(fd.get('texta'), textaValue, 'texta value mismatch')\n  t.assert.strictEqual(fd.get('textb'), textbValue, 'textb value mismatch')\n\n  const filea = fd.get('filea')\n  t.assert.strictEqual(filea.name, 'filea.txt', 'filea filename mismatch')\n  t.assert.strictEqual(filea.size, fileaContents.length, 'filea size mismatch')\n  t.assert.strictEqual(await filea.text(), fileaContents, 'filea contents mismatch')\n\n  const fileb = fd.get('fileb')\n  t.assert.strictEqual(fileb.name, 'fileb.txt', 'fileb filename mismatch')\n  t.assert.strictEqual(fileb.size, filebContents.length, 'fileb size mismatch')\n  t.assert.strictEqual(await fileb.text(), filebContents, 'fileb contents mismatch')\n})\n\ntest('ReadForm - file without name', async (t) => {\n  const response = makeResponse(messageWithFileWithoutName, boundary)\n  const fd = await response.formData()\n\n  // A file with an empty filename is treated as a field in the web platform FormData\n  const value = fd.get('hiddenfile')\n  if (typeof value === 'string') {\n    t.assert.strictEqual(value, filebContents, 'hiddenfile value mismatch')\n  } else {\n    t.assert.strictEqual(await value.text(), filebContents, 'hiddenfile contents mismatch')\n  }\n})\n\ntest('ReadForm - file with filename', async (t) => {\n  const response = makeResponse(messageWithFileName, boundary)\n  const fd = await response.formData()\n\n  const filea = fd.get('filea')\n  t.assert.strictEqual(filea.name, 'filea.txt', 'filea filename mismatch')\n  t.assert.strictEqual(await filea.text(), fileaContents, 'filea contents mismatch')\n})\n\ntest('ReadForm - text content type', async (t) => {\n  const response = makeResponse(messageWithTextContentType, boundary)\n  const fd = await response.formData()\n\n  t.assert.strictEqual(fd.get('texta'), textaValue, 'texta value mismatch')\n})\n\ntest('ReadForm - no read after EOF', async (t) => {\n  const eofBoundary = '---------------------------8d345eef0d38dc9'\n  const body = [\n    '-----------------------------8d345eef0d38dc9',\n    'Content-Disposition: form-data; name=\"version\"',\n    '',\n    '171',\n    '-----------------------------8d345eef0d38dc9--'\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, eofBoundary)\n  const fd = await response.formData()\n\n  t.assert.strictEqual(fd.get('version'), '171', 'version value mismatch')\n})\n\ntest('ReadForm - non-file max memory (large text value)', async (t) => {\n  const n = 10 << 20 // 10 MB\n  const largeTextValue = '1'.repeat(n)\n  const body = [\n    '--MyBoundary',\n    'Content-Disposition: form-data; name=\"largetext\"',\n    '',\n    largeTextValue,\n    '--MyBoundary--'\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n  const fd = await response.formData()\n\n  t.assert.strictEqual(fd.get('largetext'), largeTextValue, 'largetext value mismatch')\n})\n\ntest('ReadForm - many files', async (t) => {\n  const numFiles = 10\n  const parts = []\n  const fileBoundary = 'TestBoundary'\n\n  for (let i = 0; i < numFiles; i++) {\n    parts.push(\n      `--${fileBoundary}`,\n      `Content-Disposition: form-data; name=\"${i}\"; filename=\"${i}\"`,\n      'Content-Type: application/octet-stream',\n      '',\n      `${i}`\n    )\n  }\n  parts.push(`--${fileBoundary}--`)\n  const body = parts.join('\\r\\n')\n\n  const response = makeResponse(body, fileBoundary)\n  const fd = await response.formData()\n\n  for (let i = 0; i < numFiles; i++) {\n    const file = fd.get(`${i}`)\n    t.assert.ok(file, `file \"${i}\" should exist`)\n    t.assert.strictEqual(file.name, `${i}`, `file \"${i}\" filename mismatch`)\n    const text = await file.text()\n    t.assert.strictEqual(text, `${i}`, `file \"${i}\" contents mismatch`)\n  }\n})\n\ntest('ReadForm - limits with many values', async (t) => {\n  const numValues = 100\n  const parts = []\n  const limitBoundary = 'LimitBoundary'\n\n  for (let i = 0; i < numValues; i++) {\n    parts.push(\n      `--${limitBoundary}`,\n      `Content-Disposition: form-data; name=\"field${i}\"`,\n      '',\n      `value ${i}`\n    )\n  }\n  parts.push(`--${limitBoundary}--`)\n  const body = parts.join('\\r\\n')\n\n  const response = makeResponse(body, limitBoundary)\n  const fd = await response.formData()\n\n  for (let i = 0; i < numValues; i++) {\n    t.assert.strictEqual(\n      fd.get(`field${i}`),\n      `value ${i}`,\n      `field${i} value mismatch`\n    )\n  }\n})\n\ntest('ReadForm - limits with values and files', async (t) => {\n  const numValues = 50\n  const numFiles = 50\n  const parts = []\n  const limitBoundary = 'LimitBoundary'\n\n  for (let i = 0; i < numValues; i++) {\n    parts.push(\n      `--${limitBoundary}`,\n      `Content-Disposition: form-data; name=\"field${i}\"`,\n      '',\n      `value ${i}`\n    )\n  }\n  for (let i = 0; i < numFiles; i++) {\n    parts.push(\n      `--${limitBoundary}`,\n      `Content-Disposition: form-data; name=\"file${i}\"; filename=\"file${i}\"`,\n      'Content-Type: application/octet-stream',\n      '',\n      `value ${i}`\n    )\n  }\n  parts.push(`--${limitBoundary}--`)\n  const body = parts.join('\\r\\n')\n\n  const response = makeResponse(body, limitBoundary)\n  const fd = await response.formData()\n\n  for (let i = 0; i < numValues; i++) {\n    t.assert.strictEqual(\n      fd.get(`field${i}`),\n      `value ${i}`,\n      `field${i} value mismatch`\n    )\n  }\n  for (let i = 0; i < numFiles; i++) {\n    const file = fd.get(`file${i}`)\n    t.assert.ok(file, `file${i} should exist`)\n    const text = await file.text()\n    t.assert.strictEqual(text, `value ${i}`, `file${i} contents mismatch`)\n  }\n})\n\ntest('ReadForm - metadata too large (large field name)', async (t) => {\n  const largeName = 'a'.repeat(10 << 20)\n  const body = [\n    '--MyBoundary',\n    `Content-Disposition: form-data; name=\"${largeName}\"`,\n    '',\n    'value',\n    '--MyBoundary--'\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n\n  // Expect parsing to either succeed (implementation dependent) or throw\n  try {\n    const fd = await response.formData()\n    // If it succeeds, the value should be correct\n    t.assert.strictEqual(fd.get(largeName), 'value')\n  } catch (err) {\n    // Implementation may reject overly large metadata\n    t.assert.ok(err, 'error thrown for large metadata')\n  }\n})\n\ntest('ReadForm - metadata too large (large MIME header)', async (t) => {\n  const largeHeaderValue = 'a'.repeat(10 << 20)\n  const body = [\n    '--MyBoundary',\n    'Content-Disposition: form-data; name=\"a\"',\n    `X-Foo: ${largeHeaderValue}`,\n    '',\n    'value',\n    '--MyBoundary--'\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n\n  try {\n    const fd = await response.formData()\n    t.assert.strictEqual(fd.get('a'), 'value')\n  } catch (err) {\n    t.assert.ok(err, 'error thrown for large MIME header')\n  }\n})\n\ntest('ReadForm - metadata too large (many parts)', async (t) => {\n  const parts = []\n  const numParts = 110000\n\n  for (let i = 0; i < numParts; i++) {\n    parts.push(\n      '--MyBoundary',\n      'Content-Disposition: form-data; name=\"f\"',\n      '',\n      'v'\n    )\n  }\n  parts.push('--MyBoundary--')\n  const body = parts.join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n\n  try {\n    const fd = await response.formData()\n    // If it succeeds, check that values are present\n    t.assert.ok(fd.getAll('f').length > 0)\n  } catch (err) {\n    // Implementation may reject too many parts\n    t.assert.ok(err, 'error thrown for too many parts')\n  }\n})\n\ntest('ReadForm - endless header line (name)', async (t) => {\n  // Create a body with an extremely long header name that never ends\n  const longPrefix = 'X-' + 'X'.repeat(1 << 20)\n  const body = [\n    '--MyBoundary',\n    'Content-Disposition: form-data; name=\"a\"',\n    'Content-Type: text/plain',\n    longPrefix\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n\n  try {\n    await response.formData()\n    // If parsing succeeds (truncated or ignored), that's acceptable\n  } catch (err) {\n    t.assert.ok(err, 'error thrown for endless header line')\n  }\n})\n\ntest('ReadForm - endless header line (value)', async (t) => {\n  const longValue = 'X-Header: ' + 'X'.repeat(1 << 20)\n  const body = [\n    '--MyBoundary',\n    'Content-Disposition: form-data; name=\"a\"',\n    'Content-Type: text/plain',\n    longValue\n  ].join('\\r\\n')\n\n  const response = makeResponse(body, boundary)\n\n  try {\n    await response.formData()\n  } catch (err) {\n    t.assert.ok(err, 'error thrown for endless header value')\n  }\n})\n"
  },
  {
    "path": "test/busboy/issue-3676.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\n// https://github.com/nodejs/undici/issues/3676\ntest('Leading and trailing CRLFs are ignored', async (t) => {\n  const response = new Response([\n    '--axios-1.7.7-boundary-bPgZ9x77LfApGVUN839vui4V7\\r\\n' +\n    'Content-Disposition: form-data; name=\"file\"; filename=\"doc.txt\"\\r\\n' +\n    'Content-Type: text/plain\\r\\n' +\n    '\\r\\n' +\n    'Helloworld\\r\\n' +\n    '--axios-1.7.7-boundary-bPgZ9x77LfApGVUN839vui4V7--\\r\\n' +\n    '\\r\\n'\n  ].join(''), {\n    headers: {\n      'content-type': 'multipart/form-data; boundary=axios-1.7.7-boundary-bPgZ9x77LfApGVUN839vui4V7'\n    }\n  })\n\n  await t.assert.doesNotReject(response.formData())\n})\n"
  },
  {
    "path": "test/busboy/issue-3760.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\n// https://github.com/nodejs/undici/issues/3760\ntest('filename* parameter is parsed properly', async (t) => {\n  const response = new Response([\n    '--83d82e0d-9ced-44c0-ac79-4e66a827415b\\r\\n' +\n    'Content-Type: text/plain\\r\\n' +\n    'Content-Disposition: form-data; name=\"file\"; filename*=UTF-8\\'\\'%e2%82%ac%20rates\\r\\n' +\n    '\\r\\n' +\n    'testabc\\r\\n' +\n    '--83d82e0d-9ced-44c0-ac79-4e66a827415b--\\r\\n' +\n    '\\r\\n'\n  ].join(''), {\n    headers: {\n      'content-type': 'multipart/form-data; boundary=\"83d82e0d-9ced-44c0-ac79-4e66a827415b\"'\n    }\n  })\n\n  const fd = await response.formData()\n  t.assert.deepEqual(fd.get('file').name, '€ rates')\n})\n\ntest('whitespace after filename[*]= is ignored', async (t) => {\n  for (const response of [\n    new Response([\n      '--83d82e0d-9ced-44c0-ac79-4e66a827415b\\r\\n' +\n      'Content-Type: text/plain\\r\\n' +\n      'Content-Disposition: form-data; name=\"file\"; filename*=          utf-8\\'\\'hello\\r\\n' +\n      '\\r\\n' +\n      'testabc\\r\\n' +\n      '--83d82e0d-9ced-44c0-ac79-4e66a827415b--\\r\\n' +\n      '\\r\\n'\n    ].join(''), {\n      headers: {\n        'content-type': 'multipart/form-data; boundary=\"83d82e0d-9ced-44c0-ac79-4e66a827415b\"'\n      }\n    }),\n    new Response([\n      '--83d82e0d-9ced-44c0-ac79-4e66a827415b\\r\\n' +\n      'Content-Type: text/plain\\r\\n' +\n      'Content-Disposition: form-data; name=\"file\"; filename=        \"hello\"\\r\\n' +\n      '\\r\\n' +\n      'testabc\\r\\n' +\n      '--83d82e0d-9ced-44c0-ac79-4e66a827415b--\\r\\n' +\n      '\\r\\n'\n    ].join(''), {\n      headers: {\n        'content-type': 'multipart/form-data; boundary=\"83d82e0d-9ced-44c0-ac79-4e66a827415b\"'\n      }\n    })\n  ]) {\n    const fd = await response.formData()\n    t.assert.deepEqual(fd.get('file').name, 'hello')\n  }\n})\n"
  },
  {
    "path": "test/busboy/issue-4660.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Request } = require('../..')\n\n// https://github.com/nodejs/undici/issues/4660\ntest('unquoted attributes are parsed correctly', async (t) => {\n  const request = new Request('http://localhost', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'multipart/form-data; boundary=7427efb8-6ce7-4740-9198-d90399842641'\n    },\n    body:\n      '--7427efb8-6ce7-4740-9198-d90399842641\\r\\n' +\n      'Content-Type: text/plain; charset=utf-8\\r\\n' +\n      'Content-Disposition: form-data; name=test\\r\\n' +\n      '\\r\\n' +\n      'abc\\r\\n' +\n      '--7427efb8-6ce7-4740-9198-d90399842641--'\n  })\n\n  const fd = await request.formData()\n  t.assert.deepEqual(fd.get('test'), 'abc')\n})\n\ntest('leading spaces are allowed with quoted strings', async (t) => {\n  const request = new Request('http://localhost', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'multipart/form-data; boundary=7427efb8-6ce7-4740-9198-d90399842641'\n    },\n    body:\n      '--7427efb8-6ce7-4740-9198-d90399842641\\r\\n' +\n      'Content-Type: text/plain; charset=utf-8\\r\\n' +\n      'Content-Disposition: form-data; name=           \"test\"\\r\\n' + // <-- space between attribute name and value\n      '\\r\\n' +\n      'abc\\r\\n' +\n      '--7427efb8-6ce7-4740-9198-d90399842641--'\n  })\n\n  const fd = await request.formData()\n  t.assert.deepEqual(fd.get('test'), 'abc')\n})\n\ntest('leading spaces are allowed & ignored in unquoted strings', async (t) => {\n  const request = new Request('http://localhost', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'multipart/form-data; boundary=7427efb8-6ce7-4740-9198-d90399842641'\n    },\n    body:\n      '--7427efb8-6ce7-4740-9198-d90399842641\\r\\n' +\n      'Content-Type: text/plain; charset=utf-8\\r\\n' +\n      'Content-Disposition: form-data; name=           test\\r\\n' + // <-- space between attribute name and value\n      '\\r\\n' +\n      'abc\\r\\n' +\n      '--7427efb8-6ce7-4740-9198-d90399842641--'\n  })\n\n  const fd = await request.formData()\n  t.assert.deepEqual(fd.get('test'), 'abc')\n})\n"
  },
  {
    "path": "test/busboy/issue-4671.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Request, Response, FormData } = require('../..')\n\n// https://github.com/nodejs/undici/issues/4671\ntest('preamble and epilogue is ignored', async (t) => {\n  const request = new Request('https://example.com', {\n    method: 'POST',\n    body: (function () {\n      const formData = new FormData()\n      formData.append('a', 'b')\n      return formData\n    })()\n  })\n\n  const contentType = request.headers.get('Content-Type')\n  let bytes = await request.bytes()\n  bytes = new Uint8Array([...bytes, ...Array(10).fill(0)])\n\n  await t.test('epilogue', async () => {\n    await new Response(bytes, {\n      headers: {\n        'Content-Type': contentType\n      }\n    }).formData()\n  })\n\n  await t.test('preamble', async () => {\n    // preamble\n    bytes.set(bytes.subarray(0, -10), 10)\n    bytes.fill(0, 0, 8)\n    bytes[8] = 13\n    bytes[9] = 10\n\n    await new Response(bytes, {\n      headers: {\n        'Content-Type': contentType\n      }\n    }).formData()\n  })\n})\n"
  },
  {
    "path": "test/busboy/test-parser.js",
    "content": "'use strict'\n\n// Copyright (c) 2010-2025, Marcel Hellkamp\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// https://github.com/defnull/multipart/blob/d390c3a98a619f4445d7f340d6022b0ccad2ca8e/test/test_push_parser.py\n\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\nfunction b64decode (input) {\n  return Buffer.from(input, 'base64').toString()\n}\n\nfunction makeResponse (body, bnd) {\n  return new Response(body, {\n    headers: {\n      'content-type': `multipart/form-data; boundary=${bnd}`\n    }\n  })\n}\n\nconst legacyTests = {\n  'firefox3-2png1txt': [\n    '-----------------------------186454651713519341951581030105\\r\\n',\n    'Content-Disposition: form-data; name=\"file1\"; filename=\"anchor.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file1-data]\\r\\n',\n    '-----------------------------186454651713519341951581030105\\r\\n',\n    'Content-Disposition: form-data; name=\"file2\"; filename=\"application_edit.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file2-data]\\r\\n',\n    '-----------------------------186454651713519341951581030105\\r\\n',\n    'Content-Disposition: form-data; name=\"text\"\\r\\n',\n    '\\r\\n',\n    '[Text]\\r\\n',\n    '-----------------------------186454651713519341951581030105--\\r\\n'\n  ],\n  'firefox3-2pnglongtext': [\n    '-----------------------------14904044739787191031754711748\\r\\n',\n    'Content-Disposition: form-data; name=\"file1\"; filename=\"accept.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file1-data]\\r\\n',\n    '-----------------------------14904044739787191031754711748\\r\\n',\n    'Content-Disposition: form-data; name=\"file2\"; filename=\"add.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file2-data]\\r\\n',\n    '-----------------------------14904044739787191031754711748\\r\\n',\n    'Content-Disposition: form-data; name=\"text\"\\r\\n',\n    '\\r\\n',\n    '[Text]\\r\\n',\n    '-----------------------------14904044739787191031754711748--\\r\\n'\n  ],\n  'opera8-2png1txt': [\n    '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n    'Content-Disposition: form-data; name=\"file1\"; filename=\"arrow_branch.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file1-data]\\r\\n',\n    '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n    'Content-Disposition: form-data; name=\"file2\"; filename=\"award_star_bronze_1.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file2-data]\\r\\n',\n    '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n    'Content-Disposition: form-data; name=\"text\"\\r\\n',\n    '\\r\\n',\n    '[Text]\\r\\n',\n    '------------zEO9jQKmLc2Cq88c23Dx19--\\r\\n'\n  ],\n  'webkit3-2png1txt': [\n    '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n    'Content-Disposition: form-data; name=\"file1\"; filename=\"gtk-apply.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file1-data]\\r\\n',\n    '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n    'Content-Disposition: form-data; name=\"file2\"; filename=\"gtk-no.png\"\\r\\n',\n    'Content-Type: image/png\\r\\n',\n    '\\r\\n',\n    '[file2-data]\\r\\n',\n    '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n    'Content-Disposition: form-data; name=\"text\"\\r\\n',\n    '\\r\\n',\n    '[Text]\\r\\n',\n    '------WebKitFormBoundaryjdSFhcARk8fyGNy6--\\r\\n'\n  ],\n  'ie6-2png1txt': [\n    '-----------------------------7d91b03a20128\\r\\n',\n    'Content-Disposition: form-data; name=\"file1\"; filename=\"C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file1.png\"\\r\\n',\n    'Content-Type: image/x-png\\r\\n',\n    '\\r\\n',\n    '[file1-data]\\r\\n',\n    '-----------------------------7d91b03a20128\\r\\n',\n    'Content-Disposition: form-data; name=\"file2\"; filename=\"C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file2.png\"\\r\\n',\n    'Content-Type: image/x-png\\r\\n',\n    '\\r\\n',\n    '[file2-data]\\r\\n',\n    '-----------------------------7d91b03a20128\\r\\n',\n    'Content-Disposition: form-data; name=\"text\"\\r\\n',\n    '\\r\\n',\n    '[Text]\\r\\n',\n    '-----------------------------7d91b03a20128--\\r\\n'\n  ]\n}\n\nconst browserTestCases = {\n  'firefox3-2png1txt': {\n    data: b64decode(\n        `\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0xODY0NTQ2NTE3MTM1MTkzNDE5NTE1ODEwMzAx\nMDUNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0iZmlsZTEiOyBmaWxlbmFt\nZT0iYW5jaG9yLnBuZyINCkNvbnRlbnQtVHlwZTogaW1hZ2UvcG5nDQoNColQTkcNChoKAAAADUlI\nRFIAAAAQAAAAEAgGAAAAH/P/YQAAAARnQU1BAACvyDcFiukAAAAZdEVYdFNvZnR3YXJlAEFkb2Jl\nIEltYWdlUmVhZHlxyWU8AAABnUlEQVQ4y6VTMWvCQBS+qwEFB10KGaS1P6FDpw7SrVvzAwRRx04V\nCk4K6iAoDhLXdhFcW9qhZCk4FQoW0gp2U4lQRDAUS4hJmn5Xgg2lsQ198PHu3b3vu5d3L9S2bfIf\n47wOer1ewzTNtGEYBP48kUjkfsrb8BIAMb1cLovwRfi07wrYzcCr4/1/Am4FzzhzBGZeefR7E7vd\n7j0Iu4wYjUYDBMfD0dBiMUQfstns3toKkHgF6EgmqqruW6bFiHcsxr70awVu63Q6NiOmUinquwfM\ndF1f28CVgCRJx0jMAQ1BEFquRn7CbYVCYZVbr9dbnJMohoIh9kViu90WEW9nMpmxu4JyubyF/VEs\nFiNcgCPyoyxiu7XhCPBzdU4s652VnUccbDabPLyN2C6VSmwdhFgel5DB84AJb64mEUlvmqadTKcv\n40gkUkUsg1DjeZ7iRsrWgByP71T7/afxYrHIYry/eoBD9mxsaK4VRamFw2EBQknMAWGvRClNTpQJ\nAfkCxFNgBmiez1ipVA4hdgQcOD/TLfylKIo3vubgL/YBnIw+ioOMLtwAAAAASUVORK5CYIINCi0t\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tMTg2NDU0NjUxNzEzNTE5MzQxOTUxNTgxMDMwMTA1\nDQpDb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9ImZpbGUyIjsgZmlsZW5hbWU9\nImFwcGxpY2F0aW9uX2VkaXQucG5nIg0KQ29udGVudC1UeXBlOiBpbWFnZS9wbmcNCg0KiVBORw0K\nGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdh\ncmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJRSURBVBgZpcHda81xHMDx9+d3fudYzuYw2RaZ5yTW\nolEiuZpCSjGJFEktUUr8A6ZxQZGHmDtqdrGUXHgoeZqSp1F2bLFWjtkOB8PZzvmd7+djv5XaBRfL\n6yVmxv+QjQeu7l25uuZYJmtxM0AVU8Wpw9RQU8w51AxzDqfKhFjwq6Mjdbj1RN0Zv2ZFzaloUdwr\nL2Is4r+y7hRwxs8G5mUzPxmrwcA8hvnmjIZtcxmr3Y09hHwzJZQvOAwwNZyCYqgaThVXMFzBCD7f\nJfv8MpHiKvaV3ePV2f07fMwIiSeIGeYJJoao4HmCiIeIQzPXifY+paJqO4lZi/nWPZ/krabjvlNH\nyANMBAQiBiqgakQMCunbxHJviM9bQeZdBzHJUzKhguLJlQnf1BghAmZ4gImAgAjk++8jP56QmL2G\nXG8zsfFCz8skA1mQXKbaU3X8ISIgQsgDcun7FL7cJjFnLUMfLyLRr0SLS4hbhiup5Szd19rpFYKA\nESKICCERoS95neyHmyTmbmAodQ4vGpAfmEn6YTtTahv4ODiRkGdOCUUAAUSE/uQNfqTaKFu4jvyn\nJiIxIzcwg/SjF1RsOk9R+QJMlZCvqvwhQFdbM4XvrynIVHpfn2ZSWYyhzHS+PUtSueUC0cQ0QmpG\nyE9197TUnwzq1DnUKbXSxOb6S7xtPkjngzbGVVbzvS/FjaGt9DU8xlRRJdTCMDEzRjuyZ1FwaFe9\nj+d4eecaPd1dPxNTSlfWHm1v5y/EzBitblXp4JLZ5f6yBbOwaK5tsD+9c33jq/f8w2+mRSjOllPh\nkAAAAABJRU5ErkJggg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0xODY0NTQ2NTE3MTM1\nMTkzNDE5NTE1ODEwMzAxMDUNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0i\ndGV4dCINCg0KZXhhbXBsZSB0ZXh0DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTE4NjQ1\nNDY1MTcxMzUxOTM0MTk1MTU4MTAzMDEwNS0tDQo=`\n    ),\n    boundary: '---------------------------186454651713519341951581030105',\n    files: {\n      file1: [\n        'anchor.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGdSURBVDjLpVMxa8JAFL6rAQUHXQoZpLU/\noUOnDtKtW/MDBFHHThUKTgrqICgOEtd2EVxb2qFkKTgVChbSCnZTiVBEMBRLiEmafleCDaWxDX3w\n8e7dve+7l3cv1LZt8h/jvA56vV7DNM20YRgE/jyRSOR+ytvwEgAxvVwui/BF+LTvCtjNwKvj/X8C\nbgXPOHMEZl559HsTu93uPQi7jBiNRgMEx8PR0GIxRB+y2eze2gqQeAXoSCaqqu5bpsWIdyzGvvRr\nBW7rdDo2I6ZSKeq7B8x0XV/bwJWAJEnHSMwBDUEQWq5GfsJthUJhlVuv11uckyiGgiH2RWK73RYR\nb2cymbG7gnK5vIX9USwWI1yAI/KjLGK7teEI8HN1TizrnZWdRxxsNps8vI3YLpVKbB2EWB6XkMHz\ngAlvriYRSW+app1Mpy/jSCRSRSyDUON5nuJGytaAHI/vVPv9p/FischivL96gEP2bGxorhVFqYXD\nYQFCScwBYa9EKU1OlAkB+QLEU2AGaJ7PWKlUDiF2BBw4P9Mt/KUoije+5uAv9gGcjD6Kg4wu3AAA\nAABJRU5ErkJggg==`\n        )\n      ],\n      file2: [\n        'application_edit.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJRSURBVBgZpcHda81xHMDx9+d3fudYzuYw\n2RaZ5yTWolEiuZpCSjGJFEktUUr8A6ZxQZGHmDtqdrGUXHgoeZqSp1F2bLFWjtkOB8PZzvmd7+dj\nv5XaBRfL6yVmxv+QjQeu7l25uuZYJmtxM0AVU8Wpw9RQU8w51AxzDqfKhFjwq6Mjdbj1RN0Zv2ZF\nzaloUdwrL2Is4r+y7hRwxs8G5mUzPxmrwcA8hvnmjIZtcxmr3Y09hHwzJZQvOAwwNZyCYqgaThVX\nMFzBCD7fJfv8MpHiKvaV3ePV2f07fMwIiSeIGeYJJoao4HmCiIeIQzPXifY+paJqO4lZi/nWPZ/k\nrabjvlNHyANMBAQiBiqgakQMCunbxHJviM9bQeZdBzHJUzKhguLJlQnf1BghAmZ4gImAgAjk++8j\nP56QmL2GXG8zsfFCz8skA1mQXKbaU3X8ISIgQsgDcun7FL7cJjFnLUMfLyLRr0SLS4hbhiup5Szd\n19rpFYKAESKICCERoS95neyHmyTmbmAodQ4vGpAfmEn6YTtTahv4ODiRkGdOCUUAAUSE/uQNfqTa\nKFu4jvynJiIxIzcwg/SjF1RsOk9R+QJMlZCvqvwhQFdbM4XvrynIVHpfn2ZSWYyhzHS+PUtSueUC\n0cQ0QmpGyE9197TUnwzq1DnUKbXSxOb6S7xtPkjngzbGVVbzvS/FjaGt9DU8xlRRJdTCMDEzRjuy\nZ1FwaFe9j+d4eecaPd1dPxNTSlfWHm1v5y/EzBitblXp4JLZ5f6yBbOwaK5tsD+9c33jq/f8w2+m\nRSjOllPhkAAAAABJRU5ErkJggg==`\n        )\n      ]\n    },\n    forms: { text: 'example text' }\n  },\n\n  'firefox3-2pnglongtext': {\n    data: b64decode(\n        `\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0xNDkwNDA0NDczOTc4NzE5MTAzMTc1NDcxMTc0\nOA0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJmaWxlMSI7IGZpbGVuYW1l\nPSJhY2NlcHQucG5nIg0KQ29udGVudC1UeXBlOiBpbWFnZS9wbmcNCg0KiVBORw0KGgoAAAANSUhE\nUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUg\nSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDjLpZPrS1NhHMf9O3bOdmwDCWREIYKEUHsVJBI7mg3FvCxL\n09290jZj2EyLMnJexkgpLbPUanNOberU5taUMnHZUULMvelCtWF0sW/n7MVMEiN64AsPD8/n83uu\ncQDi/id/DBT4Dolypw/qsz0pTMbj/WHpiDgsdSUyUmeiPt2+V7SrIM+bSss8ySGdR4abQQv6lrui\n6VxsRonrGCS9VEjSQ9E7CtiqdOZ4UuTqnBHO1X7YXl6Daa4yGq7vWO1D40wVDtj4kWQbn94myPGk\nCDPdSesczE2sCZShwl8CzcwZ6NiUs6n2nYX99T1cnKqA2EKui6+TwphA5k4yqMayopU5mANV3lNQ\nTBdCMVUA9VQh3GuDMHiVcLCS3J4jSLhCGmKCjBEx0xlshjXYhApfMZRP5CyYD+UkG08+xt+4wLVQ\nZA1tzxthm2tEfD3JxARH7QkbD1ZuozaggdZbxK5kAIsf5qGaKMTY2lAU/rH5HW3PLsEwUYy+YCcE\nRmIjJpDcpzb6l7th9KtQ69fi09ePUej9l7cx2DJbD7UrG3r3afQHOyCo+V3QQzE35pvQvnAZukk5\nzL5qRL59jsKbPzdheXoBZc4saFhBS6AO7V4zqCpiawuptwQG+UAa7Ct3UT0hh9p9EnXT5Vh6t4C2\n2QaUDh6HwnECOmcO7K+6kW49DKqS2DrEZCtfuI+9GrNHg4fMHVSO5kE7nAPVkAxKBxcOzsajpS4Y\nh4ohUPPWKTUh3PaQEptIOr6BiJjcZXCwktaAGfrRIpwblqOV3YKdhfXOIvBLeREWpnd8ynsaSJoy\nESFphwTtfjN6X1jRO2+FxWtCWksqBApeiFIR9K6fiTpPiigDoadqCEag5YUFKl6Yrciw0VOlhOiv\nv/Ff8wtn0KzlebrUYwAAAABJRU5ErkJggg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0x\nNDkwNDA0NDczOTc4NzE5MTAzMTc1NDcxMTc0OA0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1k\nYXRhOyBuYW1lPSJmaWxlMiI7IGZpbGVuYW1lPSJhZGQucG5nIg0KQ29udGVudC1UeXBlOiBpbWFn\nZS9wbmcNCg0KiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK\n6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9\nW7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61Ci\njSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI\n0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsm\nahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bA\nfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM\n0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/\nHfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8\nBXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3\nWUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH6\n2kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64\nTNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJg\ngg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0xNDkwNDA0NDczOTc4NzE5MTAzMTc1NDcx\nMTc0OA0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJ0ZXh0Ig0KDQotLWxv\nbmcgdGV4dA0KLS13aXRoIGJvdW5kYXJ5DQotLWxvb2thbGlrZXMtLQ0KLS0tLS0tLS0tLS0tLS0t\nLS0tLS0tLS0tLS0tLS0xNDkwNDA0NDczOTc4NzE5MTAzMTc1NDcxMTc0OC0tDQo=`\n    ),\n    boundary: '---------------------------14904044739787191031754711748',\n    files: {\n      file1: [\n        'accept.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDjLpZPrS1NhHMf9O3bOdmwDCWRE\nIYKEUHsVJBI7mg3FvCxL09290jZj2EyLMnJexkgpLbPUanNOberU5taUMnHZUULMvelCtWF0sW/n\n7MVMEiN64AsPD8/n83uucQDi/id/DBT4Dolypw/qsz0pTMbj/WHpiDgsdSUyUmeiPt2+V7SrIM+b\nSss8ySGdR4abQQv6lrui6VxsRonrGCS9VEjSQ9E7CtiqdOZ4UuTqnBHO1X7YXl6Daa4yGq7vWO1D\n40wVDtj4kWQbn94myPGkCDPdSesczE2sCZShwl8CzcwZ6NiUs6n2nYX99T1cnKqA2EKui6+TwphA\n5k4yqMayopU5mANV3lNQTBdCMVUA9VQh3GuDMHiVcLCS3J4jSLhCGmKCjBEx0xlshjXYhApfMZRP\n5CyYD+UkG08+xt+4wLVQZA1tzxthm2tEfD3JxARH7QkbD1ZuozaggdZbxK5kAIsf5qGaKMTY2lAU\n/rH5HW3PLsEwUYy+YCcERmIjJpDcpzb6l7th9KtQ69fi09ePUej9l7cx2DJbD7UrG3r3afQHOyCo\n+V3QQzE35pvQvnAZukk5zL5qRL59jsKbPzdheXoBZc4saFhBS6AO7V4zqCpiawuptwQG+UAa7Ct3\nUT0hh9p9EnXT5Vh6t4C22QaUDh6HwnECOmcO7K+6kW49DKqS2DrEZCtfuI+9GrNHg4fMHVSO5kE7\nnAPVkAxKBxcOzsajpS4Yh4ohUPPWKTUh3PaQEptIOr6BiJjcZXCwktaAGfrRIpwblqOV3YKdhfXO\nIvBLeREWpnd8ynsaSJoyESFphwTtfjN6X1jRO2+FxWtCWksqBApeiFIR9K6fiTpPiigDoadqCEag\n5YUFKl6Yrciw0VOlhOivv/Ff8wtn0KzlebrUYwAAAABJRU5ErkJggg==`\n        )\n      ],\n      file2: [\n        'add.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlko\nqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94V\nTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpK\ntE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw\n9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0Hgl\nWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1V\nMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8\nw8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9\nXPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVP\nC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnj\nahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQy\nu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg==`\n        )\n      ]\n    },\n    forms: { text: '--long text\\r\\n--with boundary\\r\\n--lookalikes--' }\n  },\n\n  'opera8-2png1txt': {\n    data: b64decode(\n        `\nLS0tLS0tLS0tLS0tekVPOWpRS21MYzJDcTg4YzIzRHgxOQ0KQ29udGVudC1EaXNwb3NpdGlvbjog\nZm9ybS1kYXRhOyBuYW1lPSJmaWxlMSI7IGZpbGVuYW1lPSJhcnJvd19icmFuY2gucG5nIg0KQ29u\ndGVudC1UeXBlOiBpbWFnZS9wbmcNCg0KiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9h\nAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHY\nSURBVDjLlVLPS1RxHJynpVu7KEn0Vt+2l6IO5qGCIsIwCPwD6hTUaSk6REoUHeoQ0qVAMrp0COpY\n0SUIPVRgSl7ScCUTst6zIoqg0y7lvpnPt8MWKuuu29w+hxnmx8dzzmE5+l7mxk1u/a3Dd/ejDjSs\nII/m3vjJ9MF0yt93ZuTkdD0CnnMO/WOnmsxsJp3yd2zfvA3mHOa+zuHTjy/zojrvHX1YqunAZE9M\nlpUcZAaZQBNIZUg9XdPBP5wePuEO7eyGQXg29QL3jz3y1oqwbvkhCuYEOQMp/HeJohCbICMUVwr0\nDvZcOnK9u7GmQNmBQLJCgORxkneqRmAs0BFmDi0bW9E72PPda/BikwWi0OEHkNR14MrewsTAZF+l\nAAWZEH6LUCwUkUlntrS1tiG5IYlEc6LcjYjSYuncngtdhakbM5dXlhgTNEMYLqB9q49MKgsPjTBX\nntVgkDNIgmI1VY2Q7QzgJ9rx++ci3ofziBYiiELQEUAyhB/D29M3Zy+uIkDIhGYvgeKvIkbHxz6T\nevzq6ut+ANh9fldetMn80OzZVVdgLFjBQ0tpEz68jcB4ifx3pQeictVXIEETnBPCKMLEwBIZAPJD\n767V/ETGwsjzYYiC6vzEP9asLo3SGuQvAAAAAElFTkSuQmCCDQotLS0tLS0tLS0tLS16RU85alFL\nbUxjMkNxODhjMjNEeDE5DQpDb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9ImZp\nbGUyIjsgZmlsZW5hbWU9ImF3YXJkX3N0YXJfYnJvbnplXzEucG5nIg0KQ29udGVudC1UeXBlOiBp\nbWFnZS9wbmcNCg0KiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/I\nNwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLhZNNSFRR\nFIC/N++9eWMzhkl/ZJqFMQMRFvTvImkXSdKiVRAURBRRW1eZA9EqaNOiFlZEtQxKyrJwUS0K+qEQ\nzaTE/AtLHR3HmffuvafFNINDWGdz7z2c7+Nyzr2WiFAIffaMBDW1+B0diAgYgxiDiCDG4DU1QfcL\nos+fWAXGYUGIUsXiAliUFER+sBAhVCIIVB7QGtEat1oTbcwVz2LMfwR+gPg+oY0bEa3x6sHdUoVd\nniMUj0M2i/j+PwVJa2QUu7YWp34D7mqNWdNApD6Ks24dpvcL4gfJRQXevbutjI4lGRzCS9iYukPo\n5dvxVqWQvn6k/2uyoudd60LGEhG43VBGyI4j2ADZ7vDJ8DZ9Img4hw4cvO/3UZ1vH3p7lrWRLwGV\nneD4y6G84NaOYSoTVYIFIiAGvXI3OWctJv0TW03jZb5gZSfzl9YBpMcIzUwdzQsuVR9EyR3TeCqm\n6w5jZiZQMz8xsxOYzDTi50AMVngJNgrnUweRbwMPiLpHrOJDOl9Vh6HD7GyO52qa0VPj6MwUJpNC\n5mYQS/DUJLH3zzRp1cqN8YulTUyODBBzt4X6Ou870z2I8ZHsHJLLYNQ8jusQ6+2exJf9BfivKdAy\nmKZiaVdodhBRAagAjIbgzxp20lwb6Vp0jADYkQO6IpHfuoqInSJUVoE2HrpyRQ1tic2LC9p3lSHW\nPh2rJfL1MeVP2weWvHp8s3ziNZ49i1q6HrR1YHGBNnt1dG2Z++gC4TdvrqNkK1eHj7ljQ/ujHx6N\nyPw8BFIiKPmNpKar7P7xb/zyT9P+o7OYvzzYSUt8U+TzxytodixEfgN3CFlQMNAcMgAAAABJRU5E\nrkJggg0KLS0tLS0tLS0tLS0tekVPOWpRS21MYzJDcTg4YzIzRHgxOQ0KQ29udGVudC1EaXNwb3Np\ndGlvbjogZm9ybS1kYXRhOyBuYW1lPSJ0ZXh0Ig0KDQpibGFmYXNlbCDDtsOkw7wNCi0tLS0tLS0t\nLS0tLXpFTzlqUUttTGMyQ3E4OGMyM0R4MTktLQ0K`\n    ),\n    boundary: '----------zEO9jQKmLc2Cq88c23Dx19',\n    files: {\n      file1: [\n        'arrow_branch.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHYSURBVDjLlVLPS1RxHJynpVu7KEn0Vt+2\nl6IO5qGCIsIwCPwD6hTUaSk6REoUHeoQ0qVAMrp0COpY0SUIPVRgSl7ScCUTst6zIoqg0y7lvpnP\nt8MWKuuu29w+hxnmx8dzzmE5+l7mxk1u/a3Dd/ejDjSsII/m3vjJ9MF0yt93ZuTkdD0CnnMO/WOn\nmsxsJp3yd2zfvA3mHOa+zuHTjy/zojrvHX1YqunAZE9MlpUcZAaZQBNIZUg9XdPBP5wePuEO7eyG\nQXg29QL3jz3y1oqwbvkhCuYEOQMp/HeJohCbICMUVwr0DvZcOnK9u7GmQNmBQLJCgORxkneqRmAs\n0BFmDi0bW9E72PPda/BikwWi0OEHkNR14MrewsTAZF+lAAWZEH6LUCwUkUlntrS1tiG5IYlEc6Lc\njYjSYuncngtdhakbM5dXlhgTNEMYLqB9q49MKgsPjTBXntVgkDNIgmI1VY2Q7QzgJ9rx++ci3ofz\niBYiiELQEUAyhB/D29M3Zy+uIkDIhGYvgeKvIkbHxz6Tevzq6ut+ANh9fldetMn80OzZVVdgLFjB\nQ0tpEz68jcB4ifx3pQeictVXIEETnBPCKMLEwBIZAPJD767V/ETGwsjzYYiC6vzEP9asLo3SGuQv\nAAAAAElFTkSuQmCC`\n        )\n      ],\n      file2: [\n        'award_star_bronze_1.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLhZNNSFRRFIC/N++9eWMzhkl/\nZJqFMQMRFvTvImkXSdKiVRAURBRRW1eZA9EqaNOiFlZEtQxKyrJwUS0K+qEQzaTE/AtLHR3Hmffu\nvafFNINDWGdz7z2c7+Nyzr2WiFAIffaMBDW1+B0diAgYgxiDiCDG4DU1QfcLos+fWAXGYUGIUsXi\nAliUFER+sBAhVCIIVB7QGtEat1oTbcwVz2LMfwR+gPg+oY0bEa3x6sHdUoVdniMUj0M2i/j+PwVJ\na2QUu7YWp34D7mqNWdNApD6Ks24dpvcL4gfJRQXevbutjI4lGRzCS9iYukPo5dvxVqWQvn6k/2uy\noudd60LGEhG43VBGyI4j2ADZ7vDJ8DZ9Img4hw4cvO/3UZ1vH3p7lrWRLwGVneD4y6G84NaOYSoT\nVYIFIiAGvXI3OWctJv0TW03jZb5gZSfzl9YBpMcIzUwdzQsuVR9EyR3TeCqm6w5jZiZQMz8xsxOY\nzDTi50AMVngJNgrnUweRbwMPiLpHrOJDOl9Vh6HD7GyO52qa0VPj6MwUJpNC5mYQS/DUJLH3zzRp\n1cqN8YulTUyODBBzt4X6Ou870z2I8ZHsHJLLYNQ8jusQ6+2exJf9BfivKdAymKZiaVdodhBRAagA\njIbgzxp20lwb6Vp0jADYkQO6IpHfuoqInSJUVoE2HrpyRQ1tic2LC9p3lSHWPh2rJfL1MeVP2weW\nvHp8s3ziNZ49i1q6HrR1YHGBNnt1dG2Z++gC4TdvrqNkK1eHj7ljQ/ujHx6NyPw8BFIiKPmNpKar\n7P7xb/zyT9P+o7OYvzzYSUt8U+TzxytodixEfgN3CFlQMNAcMgAAAABJRU5ErkJggg==`\n        )\n      ]\n    },\n    forms: { text: 'blafasel öäü' }\n  },\n\n  'webkit3-2png1txt': {\n    data: b64decode(\n        `\nLS0tLS0tV2ViS2l0Rm9ybUJvdW5kYXJ5amRTRmhjQVJrOGZ5R055Ng0KQ29udGVudC1EaXNwb3Np\ndGlvbjogZm9ybS1kYXRhOyBuYW1lPSJmaWxlMSI7IGZpbGVuYW1lPSJndGstYXBwbHkucG5nIg0K\nQ29udGVudC1UeXBlOiBpbWFnZS9wbmcNCg0KiVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACN\niR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUA\nd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANnSURBVDiNldJ9aJVVHAfw7znPuS/PvW4405WbLWfbsBuN\nbramq5Tp7mLqIFPXINlwpAitaCAPjWKgBdXzR2TBpEZoadAyCVGndttCFNxqLXORK7x3y704NlzX\nzfs8d89znuf0R/fKk03xHvjCOZxzPpzzO4cIIZBuC6nsGYmRrwFMWVw0hxV+PDVH0gVDKvNSRgZf\nrm5+QCISOi58pY1MXhm1uHg+rPDfabqnoxJpKQ2snf/gwgKY3ut4pfodX/lTGwokRt4AgLTAkMoK\n3cz7enVJg/fyTCdGE/3gwsTo+LBu2+J82qDE6IEXyrd7YvYwbpgjyPOtQHTikvhz+NKgsNGWFhhS\nWU3uwqWPBx9aRwfjPTCFgXx5JY50tumWKbaFFS7uGQypLINKZH/tukb/kN6DSSOCFfO3oqu/3biZ\niH0ZVvjF1Np7AiVG31sdXO/P8GfhqtaLbE8BqOlBZ++xuMXFbudaljxBDnNJHbZlFwF407bFh6kr\nhFRW7Jcztlc9Uee5HD+DaWsCTy/YgbaOvZpl2Y1hhU87QVLxvpQpMfpzfeXuZfmLA/Rw1wdaZOS3\nPm7aNQDGJUZ/qatqKs5etIj03TiKQv8aaFOWOHRm30+nm4zS229DmVs6Ulm6OW/50iD9G1Hsqnrb\nt2lNwyoXYwMAPnk4N1D4aO4qEtW6wagHeZ4SfNP1mW6Zdt1c5WEE8Lll5qKCQbdiGIh/h+JlK6Wi\nxcHM4z2fb9tUtkOO6hdw3Yzi2axdON33xaxuzLSGFf7HXCA1Dav+5Nn2Kyd7DyYK5bXw0QWIJM4j\n7rqGmvKd8gwZw5D+I3K8jyGhmzj366lpi4uWOz0gEUIgpDKPxGjr/VlLanZubJknXLMYiH8Pjccw\nK26C27Oouu8tfHysWbs6HnkxrPATdwVTLaSyzW63+8BLzzX6H1lSSrtjBzFpRPBkZi0mrk3Z7Z2t\nP5xqMiruhP0PTKL5EqMnSgKr87eUvSqPGf3Ipsux53CDpie0QFjhf90NhBDiVlJ1LaqmcqXq2l/7\naU7826E94rWjQb3iXbYXgAzAC8ADwI1//zF1OkQIAUIIBSAlc6tfpkjr52XTj4SFi937eP3MmDAB\n2I5YyaT63AmyuVDHmAAQt0FOzARg/aeGhBCS3EjnCBygMwKAnXL+AdDkiZ/xYgR3AAAAAElFTkSu\nQmCCDQotLS0tLS1XZWJLaXRGb3JtQm91bmRhcnlqZFNGaGNBUms4ZnlHTnk2DQpDb250ZW50LURp\nc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9ImZpbGUyIjsgZmlsZW5hbWU9Imd0ay1uby5wbmci\nDQpDb250ZW50LVR5cGU6IGltYWdlL3BuZw0KDQqJUE5HDQoaCgAAAA1JSERSAAAAFAAAABQIBgAA\nAI2JHQ0AAAAEc0JJVAgICAh8CGSIAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAGXRFWHRTb2Z0d2Fy\nZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAzVJREFUOI2tlM9rG0cUxz8zu7OzsqhtyTIONDG2g9ue\nUnIwFEqCwYUeTC+99u5T/4FAKKUEeuh/4FPvOZXiWw3GpRRcGjW0h1KwLLe4juOspJUlS95frwft\nCkdJbh347o95bz+8mfedVSLC/zncNwUeKnVfw4YD6yncBXCgnsJeBruPRPZf952arPCBUhUL216p\ntLm0vGxmq1X3rbk5AC6CgE67nTQbjTgaDHauYOtrkfYbgV8o9SHw/crKytR7d+5YDXhzc2hjEBGy\nOCZutciU4s+nT68ajcYl8MlXIj+9AnygVMXA4draWqVWqaBLJcz09ChLBBGBXHEYImlK0G5zcHDQ\njuF2UakuyBa2l27dmqqWywxOTpAkIWq1iILgFWVxzOXREZVymaXFxSkL2wVHFw0w1m6urq7asF7H\nsZa01SINAiQIyIp7q0XaapEEAcp1CZ884Z3VVWus3Xyo1P1xlzVsvL2wYJLTUwhDdBiiHAedL1EV\n+yxCJoJkGTpJkDAkOj3l5o0b5vD4eAPYd3M7rM+WSq7qdLCAOjtD+z46y1DXgJkIZNmIHUWj3E6H\nmelp14H1cYUZ3J31fZyTE1zA7fVw+n0cERSg8v2RUS5pPqeArNtlZmGBwqtjY+skwYig80lXBCff\n5OvANFeSxzIRojge5+j8Uu9dXOD5Pt6o41jAz1W69uznMQ8wgOf79LpdNNTHwBT22r1ebDwPt0h8\nDbQAFTADGGvp9PtxCntjYAa7zW43wVpca3HyZZsJaAF0C/k+4vs0wzDJYHcMfCSyHyfJzq/n50NT\nraKVwhl1H3cCpAsphVut8tvz58M4SXaKn8X4pFzB1lG/P2gOBuhaDYxBJhqR5e8Yg56f53gwoNHr\nDa9gq+CMz7JSauoz+HgFvr1trX+vXPZKUYSbJCMTA+K6xMYw8Dx+7Pfjw+Fw+Dt8/h38ALwQkeg6\ncAaoLcLyp/BlVam1dz3PWdDaqbkjdwVpymmaZn9FUXouUn8M3zyDJvAC+PclYA6dBmpA5SO4dxM+\nmIf3fVgCGMLfz+CPf+CXPfgZCIFz4ExEkpeWfH0opZzcKYUsI38nIy5D4BK4kgnAfwLblOaQdQsS\nAAAAAElFTkSuQmCCDQotLS0tLS1XZWJLaXRGb3JtQm91bmRhcnlqZFNGaGNBUms4ZnlHTnk2DQpD\nb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9InRleHQiDQoNCnRoaXMgaXMgYW5v\ndGhlciB0ZXh0IHdpdGggw7xtbMOkw7x0cw0KLS0tLS0tV2ViS2l0Rm9ybUJvdW5kYXJ5amRTRmhj\nQVJrOGZ5R055Ni0tDQo=`\n    ),\n    boundary: '----WebKitFormBoundaryjdSFhcARk8fyGNy6',\n    files: {\n      file1: [\n        'gtk-apply.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANnSURB\nVDiNldJ9aJVVHAfw7znPuS/PvW4405WbLWfbsBuNbramq5Tp7mLqIFPXINlwpAitaCAPjWKgBdXz\nR2TBpEZoadAyCVGndttCFNxqLXORK7x3y704NlzXzfs8d89znuf0R/fKk03xHvjCOZxzPpzzO4cI\nIZBuC6nsGYmRrwFMWVw0hxV+PDVH0gVDKvNSRgZfrm5+QCISOi58pY1MXhm1uHg+rPDfabqnoxJp\nKQ2snf/gwgKY3ut4pfodX/lTGwokRt4AgLTAkMoK3cz7enVJg/fyTCdGE/3gwsTo+LBu2+J82qDE\n6IEXyrd7YvYwbpgjyPOtQHTikvhz+NKgsNGWFhhSWU3uwqWPBx9aRwfjPTCFgXx5JY50tumWKbaF\nFS7uGQypLINKZH/tukb/kN6DSSOCFfO3oqu/3biZiH0ZVvjF1Np7AiVG31sdXO/P8GfhqtaLbE8B\nqOlBZ++xuMXFbudaljxBDnNJHbZlFwF407bFh6krhFRW7Jcztlc9Uee5HD+DaWsCTy/YgbaOvZpl\n2Y1hhU87QVLxvpQpMfpzfeXuZfmLA/Rw1wdaZOS3Pm7aNQDGJUZ/qatqKs5etIj03TiKQv8aaFOW\nOHRm30+nm4zS229DmVs6Ulm6OW/50iD9G1Hsqnrbt2lNwyoXYwMAPnk4N1D4aO4qEtW6wagHeZ4S\nfNP1mW6Zdt1c5WEE8Lll5qKCQbdiGIh/h+JlK6WixcHM4z2fb9tUtkOO6hdw3Yzi2axdON33xaxu\nzLSGFf7HXCA1Dav+5Nn2Kyd7DyYK5bXw0QWIJM4j7rqGmvKd8gwZw5D+I3K8jyGhmzj366lpi4uW\nOz0gEUIgpDKPxGjr/VlLanZubJknXLMYiH8PjccwK26C27Oouu8tfHysWbs6HnkxrPATdwVTLaSy\nzW63+8BLzzX6H1lSSrtjBzFpRPBkZi0mrk3Z7Z2tP5xqMiruhP0PTKL5EqMnSgKr87eUvSqPGf3I\npsux53CDpie0QFjhf90NhBDiVlJ1LaqmcqXq2l/7aU7826E94rWjQb3iXbYXgAzAC8ADwI1//zF1\nOkQIAUIIBSAlc6tfpkjr52XTj4SFi937eP3MmDAB2I5YyaT63AmyuVDHmAAQt0FOzARg/aeGhBCS\n3EjnCBygMwKAnXL+AdDkiZ/xYgR3AAAAAElFTkSuQmCC`\n        )\n      ],\n      file2: [\n        'gtk-no.png',\n        'image/png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM1SURB\nVDiNrZTPaxtHFMc/M7uzs7KobckyDjQxtoPbnlJyMBRKgsGFHkwvvfbuU/+BQCilBHrof+BT7zmV\n4lsNxqUUXBo1tIdSsCy3uI7jrKSVJUveX68H7QpHSW4d+O6PeW8/vJn3nVUiwv853DcFHip1X8OG\nA+sp3AVwoJ7CXga7j0T2X/edmqzwgVIVC9teqbS5tLxsZqtV9625OQAugoBOu500G404Ggx2rmDr\na5H2G4FfKPUh8P3KysrUe3fuWA14c3NoYxARsjgmbrXIlOLPp0+vGo3GJfDJVyI/vQJ8oFTFwOHa\n2lqlVqmgSyXM9PQoSwQRgVxxGCJpStBuc3Bw0I7hdlGpLsgWtpdu3ZqqlssMTk6QJCFqtYiC4BVl\ncczl0RGVcpmlxcUpC9sFRxcNMNZurq6u2rBex7GWtNUiDQIkCMiKe6tF2mqRBAHKdQmfPOGd1VVr\nrN18qNT9cZc1bLy9sGCS01MIQ3QYohwHnS9RFfssQiaCZBk6SZAwJDo95eaNG+bw+HgD2HdzO6zP\nlkqu6nSwgDo7Q/s+OstQ14CZCGTZiB1Fo9xOh5npadeB9XGFGdyd9X2ckxNcwO31cPp9HBEUoPL9\nkVEuaT6ngKzbZWZhgcKrY2PrJMGIoPNJVwQn3+TrwDRXkscyEaI4Hufo/FLvXVzg+T7eqONYwM9V\nuvbs5zEPMIDn+/S6XTTUx8AU9tq9Xmw8D7dIfA20ABUwAxhr6fT7cQp7Y2AGu81uN8FaXGtx8mWb\nCWgBdAv5PuL7NMMwyWB3DHwksh8nyc6v5+dDU62ilcIZdR93AqQLKYVbrfLb8+fDOEl2ip/F+KRc\nwdZRvz9oDgboWg2MQSYakeXvGIOen+d4MKDR6w2vYKvgjM+yUmrqM/h4Bb69ba1/r1z2SlGEmyQj\nEwPiusTGMPA8fuz348PhcPg7fP4d/AC8EJHoOnAGqC3C8qfwZVWptXc9z1nQ2qm5I3cFacppmmZ/\nRVF6LlJ/DN88gybwAvj3JWAOnQZqQOUjuHcTPpiH931YAhjC38/gj3/glz34GQiBc+BMRJKXlnx9\nKKWc3CmFLCN/JyMuQ+ASuJIJwH8C25TmkHULEgAAAABJRU5ErkJggg==`\n        )\n      ]\n    },\n    forms: { text: 'this is another text with ümläüts' }\n  },\n\n  'ie6-2png1txt': {\n    data: b64decode(\n        `\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS03ZDkxYjAzYTIwMTI4DQpDb250ZW50LURpc3Bv\nc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9ImZpbGUxIjsgZmlsZW5hbWU9IkM6XFB5dGhvbjI1XHd6\ndGVzdFx3ZXJremV1Zy1tYWluXHRlc3RzXG11bHRpcGFydFxmaXJlZm94My0ycG5nMXR4dFxmaWxl\nMS5wbmciDQpDb250ZW50LVR5cGU6IGltYWdlL3gtcG5nDQoNColQTkcNChoKAAAADUlIRFIAAAAQ\nAAAAEAgGAAAAH/P/YQAAAARnQU1BAACvyDcFiukAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdl\nUmVhZHlxyWU8AAABnUlEQVQ4y6VTMWvCQBS+qwEFB10KGaS1P6FDpw7SrVvzAwRRx04VCk4K6iAo\nDhLXdhFcW9qhZCk4FQoW0gp2U4lQRDAUS4hJmn5Xgg2lsQ198PHu3b3vu5d3L9S2bfIf47wOer1e\nwzTNtGEYBP48kUjkfsrb8BIAMb1cLovwRfi07wrYzcCr4/1/Am4FzzhzBGZeefR7E7vd7j0Iu4wY\njUYDBMfD0dBiMUQfstns3toKkHgF6EgmqqruW6bFiHcsxr70awVu63Q6NiOmUinquwfMdF1f28CV\ngCRJx0jMAQ1BEFquRn7CbYVCYZVbr9dbnJMohoIh9kViu90WEW9nMpmxu4JyubyF/VEsFiNcgCPy\noyxiu7XhCPBzdU4s652VnUccbDabPLyN2C6VSmwdhFgel5DB84AJb64mEUlvmqadTKcv40gkUkUs\ng1DjeZ7iRsrWgByP71T7/afxYrHIYry/eoBD9mxsaK4VRamFw2EBQknMAWGvRClNTpQJAfkCxFNg\nBmiez1ipVA4hdgQcOD/TLfylKIo3vubgL/YBnIw+ioOMLtwAAAAASUVORK5CYIINCi0tLS0tLS0t\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tN2Q5MWIwM2EyMDEyOA0KQ29udGVudC1EaXNwb3NpdGlvbjog\nZm9ybS1kYXRhOyBuYW1lPSJmaWxlMiI7IGZpbGVuYW1lPSJDOlxQeXRob24yNVx3enRlc3Rcd2Vy\na3pldWctbWFpblx0ZXN0c1xtdWx0aXBhcnRcZmlyZWZveDMtMnBuZzF0eHRcZmlsZTIucG5nIg0K\nQ29udGVudC1UeXBlOiBpbWFnZS94LXBuZw0KDQqJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAA\nAB/z/2EAAAAEZ0FNQQAAr8g3BYrpAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccll\nPAAAAlFJREFUGBmlwd1rzXEcwPH353d+51jO5jDZFpnnJNaiUSK5mkJKMYkUSS1RSvwDpnFBkYeY\nO2p2sZRceCh5mpKnUXZssVaO2Q4Hw9nO+Z3v52O/ldoFF8vrJWbG/5CNB67uXbm65lgma3EzQBVT\nxanD1FBTzDnUDHMOp8qEWPCroyN1uPVE3Rm/ZkXNqWhR3CsvYiziv7LuFHDGzwbmZTM/GavBwDyG\n+eaMhm1zGavdjT2EfDMllC84DDA1nIJiqBpOFVcwXMEIPt8l+/wykeIq9pXd49XZ/Tt8zAiJJ4gZ\n5gkmhqjgeYKIh4hDM9eJ9j6lomo7iVmL+dY9n+StpuO+U0fIA0wEBCIGKqBqRAwK6dvEcm+Iz1tB\n5l0HMclTMqGC4smVCd/UGCECZniAiYCACOT77yM/npCYvYZcbzOx8ULPyyQDWZBcptpTdfwhIiBC\nyANy6fsUvtwmMWctQx8vItGvRItLiFuGK6nlLN3X2ukVgoARIogIIRGhL3md7IebJOZuYCh1Di8a\nkB+YSfphO1NqG/g4OJGQZ04JRQABRIT+5A1+pNooW7iO/KcmIjEjNzCD9KMXVGw6T1H5AkyVkK+q\n/CFAV1szhe+vKchUel+fZlJZjKHMdL49S1K55QLRxDRCakbIT3X3tNSfDOrUOdQptdLE5vpLvG0+\nSOeDNsZVVvO9L8WNoa30NTzGVFEl1MIwMTNGO7JnUXBoV72P53h55xo93V0/E1NKV9YebW/nL8TM\nGK1uVengktnl/rIFs7Borm2wP71zfeOr9/zDb6ZFKM6WU+GQAAAAAElFTkSuQmCCDQotLS0tLS0t\nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTdkOTFiMDNhMjAxMjgNCkNvbnRlbnQtRGlzcG9zaXRpb246\nIGZvcm0tZGF0YTsgbmFtZT0idGV4dCINCg0KaWU2IHN1Y2tzIDotLw0KLS0tLS0tLS0tLS0tLS0t\nLS0tLS0tLS0tLS0tLS03ZDkxYjAzYTIwMTI4LS0NCg==`\n    ),\n    boundary: '---------------------------7d91b03a20128',\n    files: {\n      file1: [\n        'C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file1.png',\n        'image/x-png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGdSURBVDjLpVMxa8JAFL6rAQUHXQoZpLU/\noUOnDtKtW/MDBFHHThUKTgrqICgOEtd2EVxb2qFkKTgVChbSCnZTiVBEMBRLiEmafleCDaWxDX3w\n8e7dve+7l3cv1LZt8h/jvA56vV7DNM20YRgE/jyRSOR+ytvwEgAxvVwui/BF+LTvCtjNwKvj/X8C\nbgXPOHMEZl559HsTu93uPQi7jBiNRgMEx8PR0GIxRB+y2eze2gqQeAXoSCaqqu5bpsWIdyzGvvRr\nBW7rdDo2I6ZSKeq7B8x0XV/bwJWAJEnHSMwBDUEQWq5GfsJthUJhlVuv11uckyiGgiH2RWK73RYR\nb2cymbG7gnK5vIX9USwWI1yAI/KjLGK7teEI8HN1TizrnZWdRxxsNps8vI3YLpVKbB2EWB6XkMHz\ngAlvriYRSW+app1Mpy/jSCRSRSyDUON5nuJGytaAHI/vVPv9p/FischivL96gEP2bGxorhVFqYXD\nYQFCScwBYa9EKU1OlAkB+QLEU2AGaJ7PWKlUDiF2BBw4P9Mt/KUoije+5uAv9gGcjD6Kg4wu3AAA\nAABJRU5ErkJggg==`\n        )\n      ],\n      file2: [\n        'C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file2.png',\n        'image/x-png',\n        b64decode(\n                `\niVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0\nU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJRSURBVBgZpcHda81xHMDx9+d3fudYzuYw\n2RaZ5yTWolEiuZpCSjGJFEktUUr8A6ZxQZGHmDtqdrGUXHgoeZqSp1F2bLFWjtkOB8PZzvmd7+dj\nv5XaBRfL6yVmxv+QjQeu7l25uuZYJmtxM0AVU8Wpw9RQU8w51AxzDqfKhFjwq6Mjdbj1RN0Zv2ZF\nzaloUdwrL2Is4r+y7hRwxs8G5mUzPxmrwcA8hvnmjIZtcxmr3Y09hHwzJZQvOAwwNZyCYqgaThVX\nMFzBCD7fJfv8MpHiKvaV3ePV2f07fMwIiSeIGeYJJoao4HmCiIeIQzPXifY+paJqO4lZi/nWPZ/k\nrabjvlNHyANMBAQiBiqgakQMCunbxHJviM9bQeZdBzHJUzKhguLJlQnf1BghAmZ4gImAgAjk++8j\nP56QmL2GXG8zsfFCz8skA1mQXKbaU3X8ISIgQsgDcun7FL7cJjFnLUMfLyLRr0SLS4hbhiup5Szd\n19rpFYKAESKICCERoS95neyHmyTmbmAodQ4vGpAfmEn6YTtTahv4ODiRkGdOCUUAAUSE/uQNfqTa\nKFu4jvynJiIxIzcwg/SjF1RsOk9R+QJMlZCvqvwhQFdbM4XvrynIVHpfn2ZSWYyhzHS+PUtSueUC\n0cQ0QmpGyE9197TUnwzq1DnUKbXSxOb6S7xtPkjngzbGVVbzvS/FjaGt9DU8xlRRJdTCMDEzRjuy\nZ1FwaFe9j+d4eecaPd1dPxNTSlfWHm1v5y/EzBitblXp4JLZ5f6yBbOwaK5tsD+9c33jq/f8w2+m\nRSjOllPhkAAAAABJRU5ErkJggg==`\n        )\n      ]\n    },\n    forms: { text: 'ie6 sucks :-/' }\n  }\n}\n\nconst legacyBrowserTestCases = {\n  'firefox3-2png1txt': {\n    data: [\n      '-----------------------------186454651713519341951581030105\\r\\n',\n      'Content-Disposition: form-data; name=\"file1\"; filename=\"anchor.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file1-data]\\r\\n',\n      '-----------------------------186454651713519341951581030105\\r\\n',\n      'Content-Disposition: form-data; name=\"file2\"; filename=\"application_edit.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file2-data]\\r\\n',\n      '-----------------------------186454651713519341951581030105\\r\\n',\n      'Content-Disposition: form-data; name=\"text\"\\r\\n',\n      '\\r\\n',\n      '[Text]\\r\\n',\n      '-----------------------------186454651713519341951581030105--\\r\\n'\n    ].join(''),\n    boundary: '---------------------------186454651713519341951581030105',\n    files: {\n      file1: ['anchor.png', 'image/png', '[file1-data]'],\n      file2: ['application_edit.png', 'image/png', '[file2-data]']\n    },\n    forms: { text: '[Text]' }\n  },\n  'opera8-2png1txt': {\n    data: [\n      '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n      'Content-Disposition: form-data; name=\"file1\"; filename=\"arrow_branch.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file1-data]\\r\\n',\n      '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n      'Content-Disposition: form-data; name=\"file2\"; filename=\"award_star_bronze_1.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file2-data]\\r\\n',\n      '------------zEO9jQKmLc2Cq88c23Dx19\\r\\n',\n      'Content-Disposition: form-data; name=\"text\"\\r\\n',\n      '\\r\\n',\n      '[Text]\\r\\n',\n      '------------zEO9jQKmLc2Cq88c23Dx19--\\r\\n'\n    ].join(''),\n    boundary: '----------zEO9jQKmLc2Cq88c23Dx19',\n    files: {\n      file1: ['arrow_branch.png', 'image/png', '[file1-data]'],\n      file2: ['award_star_bronze_1.png', 'image/png', '[file2-data]']\n    },\n    forms: { text: '[Text]' }\n  },\n  'webkit3-2png1txt': {\n    data: [\n      '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n      'Content-Disposition: form-data; name=\"file1\"; filename=\"gtk-apply.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file1-data]\\r\\n',\n      '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n      'Content-Disposition: form-data; name=\"file2\"; filename=\"gtk-no.png\"\\r\\n',\n      'Content-Type: image/png\\r\\n',\n      '\\r\\n',\n      '[file2-data]\\r\\n',\n      '------WebKitFormBoundaryjdSFhcARk8fyGNy6\\r\\n',\n      'Content-Disposition: form-data; name=\"text\"\\r\\n',\n      '\\r\\n',\n      '[Text]\\r\\n',\n      '------WebKitFormBoundaryjdSFhcARk8fyGNy6--\\r\\n'\n    ].join(''),\n    boundary: '----WebKitFormBoundaryjdSFhcARk8fyGNy6',\n    files: {\n      file1: ['gtk-apply.png', 'image/png', '[file1-data]'],\n      file2: ['gtk-no.png', 'image/png', '[file2-data]']\n    },\n    forms: { text: '[Text]' }\n  },\n  'ie6-2png1txt': {\n    data: [\n      '-----------------------------7d91b03a20128\\r\\n',\n      'Content-Disposition: form-data; name=\"file1\"; filename=\"C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file1.png\"\\r\\n',\n      'Content-Type: image/x-png\\r\\n',\n      '\\r\\n',\n      '[file1-data]\\r\\n',\n      '-----------------------------7d91b03a20128\\r\\n',\n      'Content-Disposition: form-data; name=\"file2\"; filename=\"C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file2.png\"\\r\\n',\n      'Content-Type: image/x-png\\r\\n',\n      '\\r\\n',\n      '[file2-data]\\r\\n',\n      '-----------------------------7d91b03a20128\\r\\n',\n      'Content-Disposition: form-data; name=\"text\"\\r\\n',\n      '\\r\\n',\n      '[Text]\\r\\n',\n      '-----------------------------7d91b03a20128--\\r\\n'\n    ].join(''),\n    boundary: '---------------------------7d91b03a20128',\n    files: {\n      file1: ['C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file1.png', 'image/x-png', '[file1-data]'],\n      file2: ['C:\\\\Python25\\\\wztest\\\\werkzeug-main\\\\tests\\\\multipart\\\\firefox3-2png1txt\\\\file2.png', 'image/x-png', '[file2-data]']\n    },\n    forms: { text: '[Text]' }\n  }\n}\n\ntest('legacy tests', async (t) => {\n  for (const [name, body] of Object.entries(legacyTests)) {\n    await t.test(`parsing ${name}`, async (t) => {\n      const response = makeResponse(body.join(''), body[0].slice(2).trim())\n\n      await t.assert.doesNotReject(response.formData())\n    })\n  }\n})\n\ntest('browser tests', async (t) => {\n  for (const [name, value] of Object.entries(browserTestCases)) {\n    await t.test(`parsing ${name}`, async (t) => {\n      const response = makeResponse(value.data, value.boundary)\n      const fd = await response.formData()\n\n      for (const [key, val] of fd.entries()) {\n        if (key in value.files) {\n          const [fileName, mimeType, body] = value.files[key]\n\n          t.assert.ok(val instanceof File)\n          t.assert.deepStrictEqual(fileName, val.name)\n          t.assert.deepStrictEqual(mimeType, val.type)\n          t.assert.deepStrictEqual(body, await val.text())\n        } else {\n          const expected = value.forms[key]\n          t.assert.deepStrictEqual(val, expected)\n        }\n      }\n    })\n  }\n})\n\ntest('legacy browser tests', async (t) => {\n  for (const [name, value] of Object.entries(legacyBrowserTestCases)) {\n    await t.test(`parsing ${name}`, async (t) => {\n      const response = makeResponse(value.data, value.boundary)\n      const fd = await response.formData()\n\n      for (const [key, val] of fd.entries()) {\n        if (key in value.files) {\n          const [fileName, mimeType, body] = value.files[key]\n\n          t.assert.ok(val instanceof File)\n          t.assert.deepStrictEqual(fileName, val.name)\n          t.assert.deepStrictEqual(mimeType, val.type)\n          t.assert.deepStrictEqual(body, await val.text())\n        } else {\n          const expected = value.forms[key]\n          t.assert.deepStrictEqual(val, expected)\n        }\n      }\n    })\n  }\n})\n"
  },
  {
    "path": "test/busboy/test-types-multipart-charsets.js",
    "content": "'use strict'\n\nconst { inspect } = require('node:util')\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\nconst input = Buffer.from([\n  '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n  'Content-Disposition: form-data; ' +\n   'name=\"upload_file_0\"; filename=\"テスト.dat\"',\n  'Content-Type: application/octet-stream',\n  '',\n  'A'.repeat(1023),\n  '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n].join('\\r\\n'))\nconst boundary = '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k'\nconst expected = [\n  {\n    type: 'file',\n    name: 'upload_file_0',\n    data: Buffer.from('A'.repeat(1023)),\n    info: {\n      filename: 'テスト.dat',\n      encoding: '7bit',\n      mimeType: 'application/octet-stream'\n    }\n  }\n]\n\ntest('unicode filename', async (t) => {\n  const response = new Response(input, {\n    headers: {\n      'content-type': `multipart/form-data; boundary=${boundary}`\n    }\n  })\n\n  const fd = await response.formData()\n  const results = []\n\n  for (const [name, value] of fd) {\n    if (typeof value === 'string') { // field\n      results.push({\n        type: 'field',\n        name,\n        val: value,\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      })\n    } else { // File\n      results.push({\n        type: 'file',\n        name,\n        data: Buffer.from(await value.arrayBuffer()),\n        info: {\n          filename: value.name,\n          encoding: '7bit',\n          mimeType: value.type\n        }\n      })\n    }\n  }\n\n  t.assert.deepStrictEqual(\n    results,\n    expected,\n    'Results mismatch.\\n' +\n      `Parsed: ${inspect(results)}\\n` +\n      `Expected: ${inspect(expected)}`\n  )\n})\n"
  },
  {
    "path": "test/busboy/test-types-multipart.js",
    "content": "'use strict'\n\nconst { inspect } = require('node:util')\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\nconst active = new Map()\n\nconst tests = [\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; name=\"file_name_0\"',\n        '',\n        'super alpha file',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; name=\"file_name_1\"',\n        '',\n        'super beta file',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_0\"; filename=\"1k_a.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'A'.repeat(1023),\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_1\"; filename=\"1k_b.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'B'.repeat(1023),\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'field',\n        name: 'file_name_0',\n        val: 'super alpha file',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      },\n      {\n        type: 'field',\n        name: 'file_name_1',\n        val: 'super beta file',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('A'.repeat(1023)),\n        info: {\n          filename: '1k_a.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_1',\n        data: Buffer.from('B'.repeat(1023)),\n        info: {\n          filename: '1k_b.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      }\n    ],\n    what: 'Fields and files'\n  },\n  {\n    source: [\n      ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n        'Content-Disposition: form-data; name=\"cont\"',\n        '',\n        'some random content',\n        '------WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n        'Content-Disposition: form-data; name=\"pass\"',\n        '',\n        'some random pass',\n        '------WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n        'Content-Disposition: form-data; name=\"bit\"',\n        '',\n        '2',\n        '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--'\n      ].join('\\r\\n')\n    ],\n    boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n    expected: [\n      {\n        type: 'field',\n        name: 'cont',\n        val: 'some random content',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      },\n      {\n        type: 'field',\n        name: 'pass',\n        val: 'some random pass',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      },\n      {\n        type: 'field',\n        name: 'bit',\n        val: '2',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      }\n    ],\n    what: 'Fields only'\n  },\n  {\n    source: [\n      ''\n    ],\n    boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n    expected: [\n      { error: 'Unexpected end of form' }\n    ],\n    what: 'No fields and no files'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_0\"; filename=\"/tmp/1k_a.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_1\"; filename=\"C:\\\\files\\\\1k_b.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_2\"; filename=\"relative/1k_c.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: '/tmp/1k_a.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_1',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: 'C:\\\\files\\\\1k_b.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_2',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: 'relative/1k_c.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      }\n    ],\n    what: 'Files with filenames containing paths preserve path'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_0\"; filename=\"/absolute/1k_a.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_1\"; filename=\"C:\\\\absolute\\\\1k_b.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_2\"; filename=\"relative/1k_c.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: '/absolute/1k_a.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_1',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: 'C:\\\\absolute\\\\1k_b.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_2',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: 'relative/1k_c.dat',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      }\n    ],\n    what: 'Paths to be preserved'\n  },\n  {\n    source: [\n      ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n        'Content-Disposition: form-data; name=\"cont\"',\n        'Content-Type: ',\n        '',\n        'some random content',\n        '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--'\n      ].join('\\r\\n')\n    ],\n    boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n    expected: [\n      {\n        type: 'field',\n        name: 'cont',\n        val: 'some random content',\n        info: {\n          encoding: '7bit',\n          mimeType: 'text/plain'\n        }\n      }\n    ],\n    what: 'Empty content-type defaults to text/plain'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"file\"; filename*=\"utf-8\\'\\'n%C3%A4me.txt\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'file',\n        data: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n        info: {\n          filename: 'utf-8\\'\\'n%C3%A4me.txt',\n          encoding: '7bit',\n          mimeType: 'application/octet-stream'\n        }\n      }\n    ],\n    what: 'Unicode filenames'\n  },\n  {\n    source: [\n      ['--asdasdasdasd\\r\\n',\n        'Content-Type: text/plain\\r\\n',\n        'Content-Disposition: form-data; name=\"foo\"\\r\\n',\n        '\\r\\n',\n        'asd\\r\\n',\n        '--asdasdasdasd--'\n      ].join(':)')\n    ],\n    boundary: 'asdasdasdasd',\n    expected: [\n      { error: 'Malformed part header' }\n    ],\n    what: 'Stopped mid-header'\n  },\n  {\n    source: [\n      ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n        'Content-Disposition: form-data; name=\"cont\"',\n        'Content-Type: application/json',\n        '',\n        '{}',\n        '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--'\n      ].join('\\r\\n')\n    ],\n    boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n    expected: [\n      {\n        type: 'field',\n        name: 'cont',\n        val: '{}',\n        info: {\n          encoding: '7bit',\n          // TODO: there's no way to get the content-type of a field\n          mimeType: 'text/plain' // 'application/json'\n        }\n      }\n    ],\n    what: 'content-type for fields'\n  },\n  {\n    source: [\n      '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--'\n    ],\n    boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY',\n    expected: [],\n    what: 'empty form'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=upload_file_0; filename=\"1k_a.dat\"',\n        'Content-Type: application/octet-stream',\n        'Content-Transfer-Encoding: binary',\n        '',\n        ''\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      { error: 'Unexpected end of form' }\n    ],\n    what: 'Stopped mid-file #1'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=upload_file_0; filename=\"1k_a.dat\"',\n        'Content-Type: application/octet-stream',\n        '',\n        'a'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      { error: 'Unexpected end of form' }\n    ],\n    what: 'Stopped mid-file #2'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_0\"; filename=\"notes.txt\"',\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'a',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('a'),\n        info: {\n          filename: 'notes.txt',\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      }\n    ],\n    what: 'Text file with charset'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         `name=\"upload_file_0\"; filename=\"${'a'.repeat(64 * 1024)}.txt\"`,\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'ab',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_1\"; filename=\"notes2.txt\"',\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'cd',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      // TODO: the RFC does not mention the max size of a filename?\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('ab'),\n        info: {\n          filename: `${'a'.repeat(64 * 1024)}.txt`,\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      }, // { error: 'Malformed part header' },\n      {\n        type: 'file',\n        name: 'upload_file_1',\n        data: Buffer.from('cd'),\n        info: {\n          filename: 'notes2.txt',\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      }\n    ],\n    what: 'Oversized part header'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         'name=\"upload_file_0\"; filename=\"notes.txt\"',\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'a'.repeat(31) + '\\r'\n      ].join('\\r\\n'),\n      'b'.repeat(40),\n      '\\r\\n-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('a'.repeat(31) + '\\r' + 'b'.repeat(40)),\n        info: {\n          filename: 'notes.txt',\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      }\n    ],\n    what: 'Lookbehind data should not stall file streams'\n  },\n  {\n    source: [\n      ['-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         `name=\"upload_file_0\"; filename=\"${'a'.repeat(8 * 1024)}.txt\"`,\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'ab',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         `name=\"upload_file_1\"; filename=\"${'b'.repeat(8 * 1024)}.txt\"`,\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'cd',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n        'Content-Disposition: form-data; ' +\n         `name=\"upload_file_2\"; filename=\"${'c'.repeat(8 * 1024)}.txt\"`,\n        'Content-Type: text/plain; charset=utf8',\n        '',\n        'ef',\n        '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'\n      ].join('\\r\\n')\n    ],\n    boundary: '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',\n    expected: [\n      {\n        type: 'file',\n        name: 'upload_file_0',\n        data: Buffer.from('ab'),\n        info: {\n          filename: `${'a'.repeat(8 * 1024)}.txt`,\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_1',\n        data: Buffer.from('cd'),\n        info: {\n          filename: `${'b'.repeat(8 * 1024)}.txt`,\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      },\n      {\n        type: 'file',\n        name: 'upload_file_2',\n        data: Buffer.from('ef'),\n        info: {\n          filename: `${'c'.repeat(8 * 1024)}.txt`,\n          encoding: '7bit',\n          mimeType: 'text/plain; charset=utf8'\n        }\n      }\n    ],\n    what: 'Large filename'\n  },\n  {\n    source: [\n      '\\r\\n--d1bf46b3-aa33-4061-b28d-6c5ced8b08ee\\r\\n',\n      'Content-Type: application/gzip\\r\\n' +\n        'Content-Encoding: gzip\\r\\n' +\n        'Content-Disposition: form-data; name=\"batch-1\"; filename=\"batch-1\"' +\n        '\\r\\n\\r\\n' +\n      '\\r\\n--d1bf46b3-aa33-4061-b28d-6c5ced8b08ee--'\n    ],\n    boundary: 'd1bf46b3-aa33-4061-b28d-6c5ced8b08ee',\n    expected: [\n      {\n        type: 'file',\n        name: 'batch-1',\n        data: Buffer.alloc(0),\n        info: {\n          filename: 'batch-1',\n          encoding: '7bit',\n          mimeType: 'application/gzip'\n        }\n      }\n    ],\n    what: 'Empty part'\n  }\n]\n\ntest('FormData parsing tests', async (t) => {\n  for (const test of tests) {\n    active.set(test, 1)\n\n    const { what, boundary, source } = test\n\n    const body = source.reduce((a, b) => a + b, '')\n    const response = new Response(body, {\n      headers: {\n        'content-type': `multipart/form-data; boundary=${boundary}`\n      }\n    })\n\n    let fd\n    const results = []\n\n    try {\n      fd = await response.formData()\n    } catch (e) {\n      results.push({ error: e.message })\n\n      if (test.expected.length === 1 && test.expected[0].error) {\n        active.delete(test)\n      }\n\n      continue\n    }\n\n    for (const [name, value] of fd) {\n      if (typeof value === 'string') { // field\n        results.push({\n          type: 'field',\n          name,\n          val: value,\n          info: {\n            encoding: '7bit',\n            mimeType: 'text/plain'\n          }\n        })\n      } else { // File\n        results.push({\n          type: 'file',\n          name,\n          data: Buffer.from(await value.arrayBuffer()),\n          info: {\n            filename: value.name,\n            encoding: '7bit',\n            mimeType: value.type\n          }\n        })\n      }\n    }\n\n    active.delete(test)\n\n    t.assert.deepStrictEqual(\n      results,\n      test.expected,\n      `[${what}] Results mismatch.\\n` +\n        `Parsed: ${inspect(results)}\\n` +\n        `Expected: ${inspect(test.expected)}`\n    )\n  }\n})\n"
  },
  {
    "path": "test/cache/cache.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Cache } = require('../../lib/web/cache/cache')\nconst { caches, Response } = require('../../')\n\ntest('constructor', (t) => {\n  t.assert.throws(() => new Cache(null), {\n    name: 'TypeError',\n    message: 'TypeError: Illegal constructor'\n  })\n})\n\n// https://github.com/nodejs/undici/issues/4710\ntest('cache.match should work after garbage collection', async (t) => {\n  const cache = await caches.open('test-gc-cache')\n\n  t.after(async () => {\n    await caches.delete('test-gc-cache')\n  })\n\n  const url = 'https://example.com/test-gc'\n  const testData = { answer: 42 }\n\n  await cache.put(url, Response.json(testData))\n\n  // Call match multiple times with GC pressure between calls\n  // The bug manifests when the temporary Response object from fromInnerResponse()\n  // is garbage collected, which triggers the FinalizationRegistry to cancel\n  // the cached stream.\n  for (let i = 0; i < 20; i++) {\n    // Create significant memory pressure to trigger GC\n    // eslint-disable-next-line no-unused-vars\n    const garbage = Array.from({ length: 30000 }, () => ({ value: Math.random() }))\n\n    // Force GC if available (run with --expose-gc)\n    if (global.gc) {\n      global.gc()\n    }\n\n    // Delay to allow FinalizationRegistry callbacks to run\n    // The bug requires time for the GC to collect the temporary Response\n    // and for the finalization callback to cancel the stream\n    await new Promise((resolve) => setTimeout(resolve, 10))\n\n    // This should not throw \"Body has already been consumed\"\n    const match = await cache.match(url)\n    t.assert.ok(match, `Iteration ${i}: match should return a response`)\n\n    const result = await match.json()\n    t.assert.deepStrictEqual(result, testData, `Iteration ${i}: response body should match`)\n  }\n})\n"
  },
  {
    "path": "test/cache/cachestorage.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { CacheStorage } = require('../../lib/web/cache/cachestorage')\n\ntest('constructor', (t) => {\n  t.assert.throws(() => new CacheStorage(null), {\n    name: 'TypeError',\n    message: 'TypeError: Illegal constructor'\n  })\n})\n"
  },
  {
    "path": "test/cache/get-field-values.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { getFieldValues } = require('../../lib/web/cache/util')\n\ntest('getFieldValues', (t) => {\n  t.assert.throws(() => getFieldValues(null), {\n    name: 'AssertionError',\n    message: 'The expression evaluated to a falsy value:\\n\\n  assert(header !== null)\\n'\n  })\n  t.assert.deepStrictEqual(getFieldValues(''), [])\n  t.assert.deepStrictEqual(getFieldValues('foo'), ['foo'])\n  t.assert.deepStrictEqual(getFieldValues('invälid'), [])\n  t.assert.deepStrictEqual(getFieldValues('foo, bar'), ['foo', 'bar'])\n  t.assert.deepStrictEqual(getFieldValues('foo, bar, baz'), ['foo', 'bar', 'baz'])\n  t.assert.deepStrictEqual(getFieldValues('foo, bar, baz, '), ['foo', 'bar', 'baz'])\n  t.assert.deepStrictEqual(getFieldValues('foo, bar, baz, , '), ['foo', 'bar', 'baz'])\n})\n"
  },
  {
    "path": "test/cache-interceptor/cache-store-test-utils.js",
    "content": "'use strict'\n\nconst { equal, notEqual, deepStrictEqual } = require('node:assert')\nconst { describe, test, after } = require('node:test')\nconst { Readable } = require('node:stream')\nconst { once } = require('node:events')\nconst FakeTimers = require('@sinonjs/fake-timers')\n\n/**\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore\n *\n * @param {{ new(...any): CacheStore }} CacheStore\n * @param {object} [options]\n * @param {boolean} [options.skip]\n */\nfunction cacheStoreTests (CacheStore, options) {\n  describe(CacheStore.prototype.constructor.name, () => {\n    test('matches interface', options, () => {\n      equal(typeof CacheStore.prototype.get, 'function')\n      equal(typeof CacheStore.prototype.createWriteStream, 'function')\n      equal(typeof CacheStore.prototype.delete, 'function')\n    })\n\n    test('caches request', options, async () => {\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const key = {\n        origin: 'localhost',\n        path: '/',\n        method: 'GET',\n        headers: {}\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const value = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 10000,\n        deleteAt: Date.now() + 20000\n      }\n\n      const body = [Buffer.from('asd'), Buffer.from('123')]\n\n      const store = new CacheStore()\n\n      // Sanity check\n      equal(await store.get(key), undefined)\n\n      // Write response to store\n      {\n        const writable = store.createWriteStream(key, value)\n        notEqual(writable, undefined)\n        writeBody(writable, body)\n      }\n\n      // Now let's try fetching the response from the store\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value, body)\n      }\n\n      /**\n       * Let's try out a request to a different resource to make sure it can\n       *  differentiate between the two\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const anotherKey = {\n        origin: 'localhost',\n        path: '/asd',\n        method: 'GET',\n        headers: {}\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const anotherValue = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 10000,\n        deleteAt: Date.now() + 20000\n      }\n\n      const anotherBody = [Buffer.from('asd'), Buffer.from('123')]\n\n      equal(store.get(anotherKey), undefined)\n\n      {\n        const writable = store.createWriteStream(anotherKey, anotherValue)\n        notEqual(writable, undefined)\n        writeBody(writable, anotherBody)\n      }\n\n      {\n        const result = await store.get(structuredClone(anotherKey))\n        notEqual(result, undefined)\n        await compareGetResults(result, anotherValue, anotherBody)\n      }\n    })\n\n    test('returns stale response before deleteAt', options, async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      after(() => clock.uninstall())\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const key = {\n        origin: 'localhost',\n        path: '/',\n        method: 'GET',\n        headers: {}\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const value = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        // deleteAt is different because stale-while-revalidate, stale-if-error, ...\n        deleteAt: Date.now() + 5000\n      }\n\n      const body = [Buffer.from('asd'), Buffer.from('123')]\n\n      const store = new CacheStore()\n\n      // Sanity check\n      equal(store.get(key), undefined)\n\n      {\n        const writable = store.createWriteStream(key, value)\n        notEqual(writable, undefined)\n        writeBody(writable, body)\n      }\n\n      clock.tick(1500)\n\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value, body)\n      }\n\n      clock.tick(6000)\n\n      // Past deleteAt, shouldn't be returned\n      equal(await store.get(key), undefined)\n    })\n\n    test('a stale request is overwritten', options, async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      after(() => clock.uninstall())\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const key = {\n        origin: 'localhost',\n        path: '/',\n        method: 'GET',\n        headers: {}\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const value = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        // deleteAt is different because stale-while-revalidate, stale-if-error, ...\n        deleteAt: Date.now() + 5000\n      }\n\n      const body = [Buffer.from('asd'), Buffer.from('123')]\n\n      const store = new CacheStore()\n\n      // Sanity check\n      equal(store.get(key), undefined)\n\n      {\n        const writable = store.createWriteStream(key, value)\n        notEqual(writable, undefined)\n        writeBody(writable, body)\n      }\n\n      clock.tick(1500)\n\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value, body)\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const value2 = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'baz' },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        // deleteAt is different because stale-while-revalidate, stale-if-error, ...\n        deleteAt: Date.now() + 5000\n      }\n\n      const body2 = [Buffer.from('foo'), Buffer.from('123')]\n\n      {\n        const writable = store.createWriteStream(key, value2)\n        notEqual(writable, undefined)\n        writeBody(writable, body2)\n      }\n\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value2, body2)\n      }\n    })\n\n    test('vary directives used to decide which response to use', options, async () => {\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const key = {\n        origin: 'localhost',\n        path: '/',\n        method: 'GET',\n        headers: {\n          'some-header': 'hello world'\n        }\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const value = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        vary: {\n          'some-header': 'hello world'\n        },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        deleteAt: Date.now() + 1000\n      }\n\n      const body = [Buffer.from('asd'), Buffer.from('123')]\n\n      const store = new CacheStore()\n\n      // Sanity check\n      equal(store.get(key), undefined)\n\n      {\n        const writable = store.createWriteStream(key, value)\n        notEqual(writable, undefined)\n        writeBody(writable, body)\n      }\n\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value, body)\n      }\n\n      /**\n       * Let's make another key to the same resource but with a different vary\n       *  header\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n       */\n      const anotherKey = {\n        origin: 'localhost',\n        path: '/',\n        method: 'GET',\n        headers: {\n          'some-header': 'hello world2'\n        }\n      }\n\n      /**\n       * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n       */\n      const anotherValue = {\n        statusCode: 200,\n        statusMessage: '',\n        headers: { foo: 'bar' },\n        vary: {\n          'some-header': 'hello world2'\n        },\n        cacheControlDirectives: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        deleteAt: Date.now() + 1000\n      }\n\n      const anotherBody = [Buffer.from('asd'), Buffer.from('123')]\n\n      equal(await store.get(anotherKey), undefined)\n\n      {\n        const writable = store.createWriteStream(anotherKey, anotherValue)\n        notEqual(writable, undefined)\n        writeBody(writable, anotherBody)\n      }\n\n      {\n        const result = await store.get(structuredClone(key))\n        notEqual(result, undefined)\n        await compareGetResults(result, value, body)\n      }\n\n      {\n        const result = await store.get(structuredClone(anotherKey))\n        notEqual(result, undefined)\n        await compareGetResults(result, anotherValue, anotherBody)\n      }\n    })\n  })\n}\n\n/**\n * @param {import('node:stream').Writable} stream\n * @param {Buffer[]} body\n */\nfunction writeBody (stream, body) {\n  for (const chunk of body) {\n    stream.write(chunk)\n  }\n\n  stream.end()\n  return stream\n}\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} param0\n * @returns {Promise<Buffer[] | undefined>}\n */\nasync function readBody ({ body }) {\n  if (!body) {\n    return undefined\n  }\n\n  if (typeof body === 'string') {\n    return [Buffer.from(body)]\n  }\n\n  if (body.constructor.name === 'Buffer') {\n    return [body]\n  }\n\n  const stream = Readable.from(body)\n\n  /**\n   * @type {Buffer[]}\n   */\n  const streamedBody = []\n\n  stream.on('data', chunk => {\n    streamedBody.push(Buffer.from(chunk))\n  })\n\n  await once(stream, 'end')\n\n  return streamedBody\n}\n\n/**\n * @param {Buffer[]} buffers\n * @returns {Buffer}\n */\nfunction joinBufferArray (buffers) {\n  const data = []\n\n  for (const buffer of buffers) {\n    buffer.forEach((chunk) => {\n      data.push(chunk)\n    })\n  }\n\n  return Buffer.from(data)\n}\n\n/**\n * @param {import('../../types/cache-interceptor.d.ts').default.GetResult} actual\n * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} expected\n * @param {Buffer[]} expectedBody\n*/\nasync function compareGetResults (actual, expected, expectedBody) {\n  const actualBody = await readBody(actual)\n  deepStrictEqual(\n    actualBody ? joinBufferArray(actualBody) : undefined,\n    joinBufferArray(expectedBody)\n  )\n\n  for (const key of Object.keys(expected)) {\n    deepStrictEqual(actual[key], expected[key])\n  }\n}\n\nmodule.exports = {\n  cacheStoreTests,\n  writeBody,\n  readBody,\n  compareGetResults\n}\n"
  },
  {
    "path": "test/cache-interceptor/cache-tests-worker.mjs",
    "content": "'use strict'\n\nimport { styleText } from 'node:util'\nimport { exit } from 'node:process'\nimport { getResults, runTests as runTestSuite } from '../fixtures/cache-tests/test-engine/client/runner.mjs'\nimport { determineTestResult, testLookup } from '../fixtures/cache-tests/test-engine/lib/results.mjs'\nimport tests from '../fixtures/cache-tests/tests/index.mjs'\nimport { Agent, fetch, interceptors, setGlobalDispatcher } from '../../index.js'\nimport { runtimeFeatures } from '../../lib/util/runtime-features.js'\nimport MemoryCacheStore from '../../lib/cache/memory-cache-store.js'\n\nif (!process.env.TEST_ENVIRONMENT) {\n  throw new Error('missing TEST_ENVIRONMENT')\n}\n\nif (!process.env.BASE_URL) {\n  throw new Error('missing BASE_URL')\n}\n\n/**\n * @type {import('./cache-tests.mjs').TestEnvironment}\n */\nconst environment = JSON.parse(process.env.TEST_ENVIRONMENT)\nif (environment.cacheStore) {\n  environment.opts.store = await makeCacheStore(environment.cacheStore)\n}\n\n// Start the test server\nawait import('../fixtures/cache-tests/test-engine/server/server.mjs')\n\n// Output the testing setup\nconsole.log('TEST ENVIRONMENT')\nconsole.log(`       BASE_URL: ${styleText('gray', process.env.BASE_URL)}`)\nif (environment.opts.store) {\n  console.log(`          store: ${styleText('gray', environment.opts.store?.constructor.name ?? 'undefined')}`)\n}\nif (environment.opts.methods) {\n  console.log(`        methods: ${styleText('gray', JSON.stringify(environment.opts.methods) ?? 'undefined')}`)\n}\nif (environment.opts.cacheByDefault) {\n  console.log(` cacheByDefault: ${styleText('gray', `${environment.opts.cacheByDefault}`)}`)\n}\nif (environment.opts.type) {\n  console.log(`           type: ${styleText('gray', environment.opts.type)}`)\n}\nif (environment.ignoredTests) {\n  console.log(`  ignored tests: ${styleText('gray', JSON.stringify(environment.ignoredTests))}`)\n}\n\n// Setup the client\nconst client = new Agent().compose(interceptors.cache(environment.opts))\nsetGlobalDispatcher(client)\n\nglobalThis.fetch = fetch\n\n// Run the suite\nawait runTestSuite(tests, true, process.env.BASE_URL)\n\nlet exitCode = 0\n\n// Print the results\nconst stats = printResults(environment, getResults())\nprintStats(stats)\n\nexit(exitCode)\n\n/**\n * @param {import('./cache-tests.mjs').TestEnvironment['cacheStore']} type\n * @returns {Promise<import('../../types/cache-interceptor').default.CacheStore>}\n */\nasync function makeCacheStore (type) {\n  const stores = {\n    MemoryCacheStore\n  }\n\n  if (runtimeFeatures.has('sqlite')) {\n    const { default: SqliteCacheStore } = await import('../../lib/cache/sqlite-cache-store.js')\n    stores.SqliteCacheStore = SqliteCacheStore\n  }\n\n  const Store = stores[type]\n  if (!Store) {\n    throw new TypeError(`unknown cache store: ${type}`)\n  }\n\n  return new Store()\n}\n\n/**\n * @param {import('./cache-tests.mjs').TestEnvironment} environment\n * @param {any} results\n * @returns {import('./cache-tests.mjs').TestStats}\n */\nfunction printResults (environment, results) {\n  /**\n   * @type {import('./cache-tests.mjs').TestStats}\n   */\n  const stats = {\n    total: Object.keys(results).length - (environment.ignoredTests?.length || 0),\n    skipped: 0,\n    passed: 0,\n    failed: 0,\n    optionalFailed: 0,\n    setupFailed: 0,\n    testHarnessFailed: 0,\n    dependencyFailed: 0,\n    retried: 0\n  }\n\n  for (const testId in results) {\n    if (environment.ignoredTests?.includes(testId)) {\n      continue\n    }\n\n    const test = testLookup(tests, testId)\n    // eslint-disable-next-line no-unused-vars\n    const [code, _, icon] = determineTestResult(tests, testId, results, false)\n\n    let status\n    let color\n    switch (code) {\n      case '-':\n        status = 'skipped'\n        color = 'gray'\n        stats.skipped++\n        break\n      case '\\uf058':\n        status = 'pass'\n        color = 'green'\n        stats.passed++\n        break\n      case '\\uf057':\n        status = 'failed'\n        color = 'red'\n        stats.failed++\n        exitCode = 1\n        break\n      case '\\uf05a':\n        status = 'failed (optional)'\n        color = 'yellow'\n        stats.optionalFailed++\n        break\n      case '\\uf055':\n        status = 'yes'\n        color = 'green'\n        stats.passed++\n        break\n      case '\\uf056':\n        status = 'no'\n        color = 'yellow'\n        stats.optionalFailed++\n        break\n      case '\\uf059':\n        status = 'setup failure'\n        color = 'red'\n        stats.setupFailed++\n        break\n      case '\\uf06a':\n        status = 'test harness failure'\n        color = 'red'\n        stats.testHarnessFailed++\n        break\n      case '\\uf192':\n        status = 'dependency failure'\n        color = 'red'\n        stats.dependencyFailed++\n        break\n      case '\\uf01e':\n        status = 'retry'\n        color = 'yellow'\n        stats.retried++\n        break\n      default:\n        status = 'unknown'\n        color = ['strikethrough', 'white']\n        break\n    }\n\n    if (process.env.CI && status !== 'failed') {\n      continue\n    }\n\n    console.log(`${icon} ${styleText(color, `${status} - ${test.name}`)} (${styleText('gray', testId)})`)\n    if (results[testId] !== true) {\n      const [type, message] = results[testId]\n      console.log(`    ${styleText(color, `${type}: ${message}`)}`)\n    }\n  }\n\n  return stats\n}\n\n/**\n * @param {import('./cache-tests.mjs').TestStats} stats\n */\nfunction printStats (stats) {\n  const {\n    total,\n    skipped,\n    passed,\n    failed,\n    optionalFailed,\n    setupFailed,\n    testHarnessFailed,\n    dependencyFailed,\n    retried\n  } = stats\n\n  if (total < 0) {\n    throw new Error('Total tests cannot be negative')\n  }\n\n  console.log(`\\n        Total tests: ${total}`)\n  console.log(`            ${styleText('gray', 'Skipped')}: ${skipped} (${((skipped / total) * 100).toFixed(1)}%)`)\n  console.log(`             ${styleText('green', 'Passed')}: ${passed} (${((passed / total) * 100).toFixed(1)}%)`)\n  console.log(`             ${styleText('red', 'Failed')}: ${failed} (${((failed / total) * 100).toFixed(1)}%)`)\n  console.log(`  ${styleText('yellow', 'Failed (optional)')}: ${optionalFailed} (${((optionalFailed / total) * 100).toFixed(1)}%)`)\n  console.log(`       ${styleText('red', 'Setup failed')}: ${setupFailed} (${((setupFailed / total) * 100).toFixed(1)}%)`)\n  console.log(`${styleText('red', 'Test Harness Failed')}: ${testHarnessFailed} (${((testHarnessFailed / total) * 100).toFixed(1)}%)`)\n  console.log(`  ${styleText('red', 'Dependency Failed')}: ${dependencyFailed} (${((dependencyFailed / total) * 100).toFixed(1)}%)`)\n  console.log(`            ${styleText('yellow', 'Retried')}: ${retried} (${((retried / total) * 100).toFixed(1)}%)`)\n}\n"
  },
  {
    "path": "test/cache-interceptor/cache-tests.mjs",
    "content": "'use strict'\n\nimport { parseArgs, styleText } from 'node:util'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { exit } from 'node:process'\nimport { fork } from 'node:child_process'\nimport { runtimeFeatures } from '../../lib/util/runtime-features.js'\n\n/**\n * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheOptions} CacheOptions\n *\n * @typedef {{\n *  opts: CacheOptions,\n *  ignoredTests?: string[],\n *  cacheStore?: 'MemoryCacheStore' | 'SqliteCacheStore'\n * }} TestEnvironment\n *\n * @typedef {{\n *  total: number,\n *  skipped: number,\n *  passed: number,\n *  failed: number,\n *  optionalFailed: number,\n *  setupFailed: number,\n *  testHarnessFailed: number,\n *  dependencyFailed: number,\n *  retried: number\n * }} TestStats\n */\n\nconst CLI_OPTIONS = parseArgs({\n  options: {\n    // Cache type(s) to test\n    type: {\n      type: 'string',\n      multiple: true,\n      short: 't'\n    },\n    // Cache store(s) to test\n    store: {\n      type: 'string',\n      multiple: true,\n      short: 's'\n    },\n    // Only shows errors\n    ci: {\n      type: 'boolean'\n    }\n  }\n})\n\n/**\n * @type {TestEnvironment}\n */\nconst BASE_TEST_ENVIRONMENT = {\n  opts: { methods: ['GET', 'HEAD'] },\n  ignoredTests: [\n    // Tests for invalid etags, goes against the spec\n    'conditional-etag-forward-unquoted',\n    'conditional-etag-strong-generate-unquoted',\n\n    // Responses with no-cache can be reused if they're revalidated (which is\n    //  what we're doing)\n    'cc-resp-no-cache',\n    'cc-resp-no-cache-case-insensitive',\n\n    // We're not caching 304s currently\n    '304-etag-update-response-Cache-Control',\n    '304-etag-update-response-Content-Foo',\n    '304-etag-update-response-Test-Header',\n    '304-etag-update-response-X-Content-Foo',\n    '304-etag-update-response-X-Test-Header',\n\n    // We just trim whatever's in the decimal place off (i.e. 7200.0 -> 7200)\n    'age-parse-float',\n\n    // Broken?\n    'head-200-update',\n    'head-200-retain',\n    'head-410-update',\n    'stale-close-must-revalidate',\n    'stale-close-no-cache'\n  ]\n}\n\n/**\n * @type {TestEnvironment[]}\n */\nconst CACHE_TYPES = [\n  {\n    opts: { type: 'shared' },\n    ignoredTests: [\n      'freshness-max-age-s-maxage-private',\n      'freshness-max-age-s-maxage-private-multiple'\n    ]\n  },\n  {\n    opts: { type: 'private' }\n  }\n]\n\n/**\n * @type {TestEnvironment[]}\n */\nconst CACHE_STORES = [\n  { opts: {}, cacheStore: 'MemoryCacheStore' }\n]\n\nif (runtimeFeatures.has('sqlite')) {\n  CACHE_STORES.push({ opts: {}, cacheStore: 'SqliteCacheStore' })\n} else {\n  console.warn('Skipping SqliteCacheStore, node:sqlite not present')\n}\n\nconst PROTOCOL = 'http'\nconst PORT = 8000\n\nconst testEnvironments = filterEnvironments(\n  buildTestEnvironments(0, [CACHE_TYPES, CACHE_STORES])\n)\n\nconsole.log(`Testing ${testEnvironments.length} environments\\n`)\nconsole.log(`PROTOCOL: ${styleText('gray', PROTOCOL)}`)\nconsole.log('')\n\n/**\n * @type {Array<Promise<[number, Array<Buffer>]>>}\n */\nconst results = []\n\n// Run all the tests in child processes because the test runner is a bit finicky\nfor (let i = 0; i < testEnvironments.length; i++) {\n  const environment = testEnvironments[i]\n  const port = PORT + i\n\n  const promise = new Promise((resolve) => {\n    const cacheTestsWorkerProcess = fork(join(import.meta.dirname, 'cache-tests-worker.mjs'), {\n      stdio: 'pipe',\n      env: {\n        NODE_OPTIONS: process.env.NODE_OPTIONS,\n        NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,\n        TEST_ENVIRONMENT: JSON.stringify(environment),\n        BASE_URL: `${PROTOCOL}://localhost:${port}`,\n        CI: CLI_OPTIONS.values.ci ? 'true' : undefined,\n        npm_config_protocol: PROTOCOL,\n        npm_config_port: `${port}`,\n        npm_config_pidfile: join(tmpdir(), `http-cache-test-server-${i}.pid`)\n      }\n    })\n\n    const stdout = []\n    cacheTestsWorkerProcess.stdout.on('data', chunk => {\n      stdout.push(chunk)\n    })\n\n    cacheTestsWorkerProcess.stderr.on('error', chunk => {\n      stdout.push(chunk)\n    })\n\n    cacheTestsWorkerProcess.on('close', code => {\n      resolve([code, stdout])\n    })\n  })\n\n  results.push(promise)\n}\n\n// Status code so we can fail CI jobs if we need\nlet exitCode = 0\n\n// Print the results of all the results in the order that they exist\nfor (const [code, stdout] of await Promise.all(results)) {\n  exitCode = code\n\n  for (const line of stdout) {\n    process.stdout.write(line)\n  }\n\n  console.log('')\n}\n\nexit(exitCode)\n\n/**\n * @param {number} idx\n * @param  {TestEnvironment[][]} testOptions\n * @returns {TestEnvironment[]}\n */\nfunction buildTestEnvironments (idx, testOptions) {\n  let baseEnvironments = testOptions[idx]\n\n  if (idx === 0) {\n    // We're at the beginning\n    baseEnvironments = baseEnvironments.map(\n      environment => joinEnvironments(BASE_TEST_ENVIRONMENT, environment))\n  }\n\n  if (idx + 1 >= testOptions.length) {\n    // We're at the end, nothing more to make a matrix out of\n    return baseEnvironments\n  }\n\n  /**\n   * @type {TestEnvironment[]}\n   */\n  const environments = []\n\n  // Get all of the environments below us\n  const subEnvironments = buildTestEnvironments(idx + 1, testOptions)\n\n  for (const baseEnvironment of baseEnvironments) {\n    const combinedEnvironments = subEnvironments.map(\n      subEnvironment => joinEnvironments(baseEnvironment, subEnvironment))\n\n    environments.push(...combinedEnvironments)\n  }\n\n  return environments\n}\n\n/**\n * @param {TestEnvironment} base\n * @param {TestEnvironment} sub\n * @returns {TestEnvironment}\n */\nfunction joinEnvironments (base, sub) {\n  const ignoredTests = base.ignoredTests ?? []\n  if (sub.ignoredTests) {\n    ignoredTests.push(...sub.ignoredTests)\n  }\n\n  return {\n    opts: {\n      ...base.opts,\n      ...sub.opts\n    },\n    ignoredTests: ignoredTests.length > 0 ? ignoredTests : undefined,\n    cacheStore: sub.cacheStore\n  }\n}\n\n/**\n * @param {TestEnvironment[]} environments\n * @returns {TestEnvironment[]}\n */\nfunction filterEnvironments (environments) {\n  const { values } = CLI_OPTIONS\n\n  if (values.type) {\n    environments = environments.filter(env =>\n      env.opts.type === undefined ||\n      values.type?.includes(env.opts.type)\n    )\n  }\n\n  if (values.store) {\n    environments = environments.filter(({ cacheStore }) => {\n      if (cacheStore === undefined) {\n        return false\n      }\n\n      const storeName = cacheStore\n      for (const allowedStore of values.store) {\n        if (storeName.match(allowedStore)) {\n          return true\n        }\n      }\n\n      return false\n    })\n  }\n\n  return environments\n}\n"
  },
  {
    "path": "test/cache-interceptor/cache-utils.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { normalizeHeaders } = require('../../lib/util/cache')\n\ntest('normalizeHeaders handles plain object headers with polluted Object.prototype[Symbol.iterator]', (t) => {\n  const { strictEqual } = tspl(t, { plan: 2 })\n\n  const originalIterator = Object.prototype[Symbol.iterator]\n  // eslint-disable-next-line no-extend-native\n  Object.prototype[Symbol.iterator] = function * () {}\n\n  try {\n    const headers = normalizeHeaders({\n      headers: {\n        Authorization: 'Bearer token',\n        'X-Test': 'ok'\n      }\n    })\n\n    strictEqual(headers.authorization, 'Bearer token')\n    strictEqual(headers['x-test'], 'ok')\n  } finally {\n    if (originalIterator === undefined) {\n      delete Object.prototype[Symbol.iterator]\n    } else {\n      // eslint-disable-next-line no-extend-native\n      Object.prototype[Symbol.iterator] = originalIterator\n    }\n  }\n})\n\ntest('normalizeHeaders handles headers from Map', (t) => {\n  const { strictEqual } = tspl(t, { plan: 1 })\n\n  const headers = normalizeHeaders({\n    headers: new Map([\n      ['X-Test', 'ok']\n    ])\n  })\n\n  strictEqual(headers['x-test'], 'ok')\n})\n"
  },
  {
    "path": "test/cache-interceptor/memory-cache-store-tests.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { equal } = require('node:assert')\nconst MemoryCacheStore = require('../../lib/cache/memory-cache-store')\nconst { cacheStoreTests } = require('./cache-store-test-utils.js')\n\ncacheStoreTests(MemoryCacheStore)\n\ntest('default limits prevent memory leaks', async () => {\n  const store = new MemoryCacheStore() // Uses new defaults\n\n  // Test that maxCount default (1024) is enforced\n  for (let i = 0; i < 1025; i++) {\n    const writeStream = store.createWriteStream(\n      { origin: 'test', path: `/test-${i}`, method: 'GET' },\n      {\n        statusCode: 200,\n        statusMessage: 'OK',\n        headers: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 60000,\n        deleteAt: Date.now() + 120000\n      }\n    )\n    writeStream.write('test data')\n    writeStream.end()\n  }\n\n  // Should be full after exceeding maxCount default of 1024\n  equal(store.isFull(), true, 'Store should be full after exceeding maxCount default')\n})\n\ntest('default maxEntrySize prevents large entries', async () => {\n  const store = new MemoryCacheStore() // Uses new defaults\n\n  // Create entry larger than default maxEntrySize (5MB)\n  const largeData = Buffer.allocUnsafe(5242881) // 5MB + 1 byte\n\n  const writeStream = store.createWriteStream(\n    { origin: 'test', path: '/large', method: 'GET' },\n    {\n      statusCode: 200,\n      statusMessage: 'OK',\n      headers: {},\n      cachedAt: Date.now(),\n      staleAt: Date.now() + 60000,\n      deleteAt: Date.now() + 120000\n    }\n  )\n\n  writeStream.write(largeData)\n  writeStream.end()\n\n  // Entry should not be cached due to maxEntrySize limit\n  const result = store.get({ origin: 'test', path: '/large', method: 'GET', headers: {} })\n  equal(result, undefined, 'Large entry should not be cached due to maxEntrySize limit')\n})\n\ntest('size getter returns correct total size', async () => {\n  const store = new MemoryCacheStore()\n  const testData = 'test data'\n\n  equal(store.size, 0, 'Initial size should be 0')\n\n  const writeStream = store.createWriteStream(\n    { origin: 'test', path: '/', method: 'GET' },\n    {\n      statusCode: 200,\n      statusMessage: 'OK',\n      headers: {},\n      cachedAt: Date.now(),\n      staleAt: Date.now() + 1000,\n      deleteAt: Date.now() + 2000\n    }\n  )\n\n  writeStream.write(testData)\n  writeStream.end()\n\n  equal(store.size, testData.length, 'Size should match written data length')\n})\n\ntest('isFull returns false when under limits', () => {\n  const store = new MemoryCacheStore({\n    maxSize: 1000,\n    maxCount: 10\n  })\n\n  equal(store.isFull(), false, 'Should not be full when empty')\n})\n\ntest('isFull returns true when maxSize reached', async () => {\n  const maxSize = 10\n  const store = new MemoryCacheStore({ maxSize })\n  const testData = 'x'.repeat(maxSize + 1) // Exceed maxSize\n\n  const writeStream = store.createWriteStream(\n    { origin: 'test', path: '/', method: 'GET' },\n    {\n      statusCode: 200,\n      statusMessage: 'OK',\n      headers: {},\n      cachedAt: Date.now(),\n      staleAt: Date.now() + 1000,\n      deleteAt: Date.now() + 2000\n    }\n  )\n\n  writeStream.write(testData)\n  writeStream.end()\n\n  equal(store.isFull(), true, 'Should be full when maxSize exceeded')\n})\n\ntest('isFull returns true when maxCount reached', async () => {\n  const maxCount = 2\n  const store = new MemoryCacheStore({ maxCount })\n\n  // Add maxCount + 1 entries\n  for (let i = 0; i <= maxCount; i++) {\n    const writeStream = store.createWriteStream(\n      { origin: 'test', path: `/${i}`, method: 'GET' },\n      {\n        statusCode: 200,\n        statusMessage: 'OK',\n        headers: {},\n        cachedAt: Date.now(),\n        staleAt: Date.now() + 1000,\n        deleteAt: Date.now() + 2000\n      }\n    )\n    writeStream.end('test')\n  }\n\n  equal(store.isFull(), true, 'Should be full when maxCount exceeded')\n})\n\ntest('emits maxSizeExceeded event when limits exceeded', async () => {\n  const maxSize = 10\n  const store = new MemoryCacheStore({ maxSize })\n\n  let eventFired = false\n  let eventPayload = null\n\n  store.on('maxSizeExceeded', (payload) => {\n    eventFired = true\n    eventPayload = payload\n  })\n\n  const testData = 'x'.repeat(maxSize + 1) // Exceed maxSize\n\n  const writeStream = store.createWriteStream(\n    { origin: 'test', path: '/', method: 'GET' },\n    {\n      statusCode: 200,\n      statusMessage: 'OK',\n      headers: {},\n      cachedAt: Date.now(),\n      staleAt: Date.now() + 1000,\n      deleteAt: Date.now() + 2000\n    }\n  )\n\n  writeStream.write(testData)\n  writeStream.end()\n\n  equal(eventFired, true, 'maxSizeExceeded event should fire')\n  equal(typeof eventPayload, 'object', 'Event should have payload')\n  equal(typeof eventPayload.size, 'number', 'Payload should have size')\n  equal(typeof eventPayload.maxSize, 'number', 'Payload should have maxSize')\n  equal(typeof eventPayload.count, 'number', 'Payload should have count')\n  equal(typeof eventPayload.maxCount, 'number', 'Payload should have maxCount')\n})\n"
  },
  {
    "path": "test/cache-interceptor/sqlite-cache-store-tests.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { notEqual, strictEqual, deepStrictEqual } = require('node:assert')\nconst { rm } = require('node:fs/promises')\nconst { cacheStoreTests, writeBody, compareGetResults } = require('./cache-store-test-utils.js')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features.js')\n\nconst SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')\ncacheStoreTests(SqliteCacheStore, { skip: runtimeFeatures.has('sqlite') === false })\n\ntest('SqliteCacheStore works nicely with multiple stores', { skip: runtimeFeatures.has('sqlite') === false }, async (t) => {\n  const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')\n  const sqliteLocation = 'cache-interceptor.sqlite'\n\n  const storeA = new SqliteCacheStore({\n    location: sqliteLocation\n  })\n\n  const storeB = new SqliteCacheStore({\n    location: sqliteLocation\n  })\n\n  t.after(async () => {\n    storeA.close()\n    storeB.close()\n    await rm(sqliteLocation)\n  })\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n   */\n  const key = {\n    origin: 'localhost',\n    path: '/',\n    method: 'GET',\n    headers: {}\n  }\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n   */\n  const value = {\n    statusCode: 200,\n    statusMessage: '',\n    headers: { foo: 'bar' },\n    cachedAt: Date.now(),\n    staleAt: Date.now() + 10000,\n    deleteAt: Date.now() + 20000\n  }\n\n  const body = [Buffer.from('asd'), Buffer.from('123')]\n\n  {\n    const writable = storeA.createWriteStream(key, value)\n    notEqual(writable, undefined)\n    writeBody(writable, body)\n  }\n\n  // Make sure we got the expected response from store a\n  {\n    const result = storeA.get(structuredClone(key))\n    notEqual(result, undefined)\n    await compareGetResults(result, value, body)\n  }\n\n  // Make sure we got the expected response from store b\n  {\n    const result = storeB.get(structuredClone(key))\n    notEqual(result, undefined)\n    await compareGetResults(result, value, body)\n  }\n})\n\ntest('SqliteCacheStore maxEntries', { skip: runtimeFeatures.has('sqlite') === false }, async () => {\n  const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')\n\n  const store = new SqliteCacheStore({\n    maxCount: 10\n  })\n\n  for (let i = 0; i < 20; i++) {\n    /**\n     * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n     */\n    const key = {\n      origin: 'localhost',\n      path: '/' + i,\n      method: 'GET',\n      headers: {}\n    }\n\n    /**\n     * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n     */\n    const value = {\n      statusCode: 200,\n      statusMessage: '',\n      headers: { foo: 'bar' },\n      cachedAt: Date.now(),\n      staleAt: Date.now() + 10000,\n      deleteAt: Date.now() + 20000\n    }\n\n    const body = ['asd', '123']\n\n    const writable = store.createWriteStream(key, value)\n    notEqual(writable, undefined)\n    writeBody(writable, body)\n  }\n\n  strictEqual(store.size <= 11, true)\n})\n\ntest('SqliteCacheStore two writes', { skip: runtimeFeatures.has('sqlite') === false }, async () => {\n  const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')\n\n  const store = new SqliteCacheStore({\n    maxCount: 10\n  })\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n   */\n  const key = {\n    origin: 'localhost',\n    path: '/',\n    method: 'GET',\n    headers: {}\n  }\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}\n   */\n  const value = {\n    statusCode: 200,\n    statusMessage: '',\n    headers: { foo: 'bar' },\n    cachedAt: Date.now(),\n    staleAt: Date.now() + 10000,\n    deleteAt: Date.now() + 20000\n  }\n\n  const body = ['asd', '123']\n\n  {\n    const writable = store.createWriteStream(key, value)\n    notEqual(writable, undefined)\n    writeBody(writable, body)\n  }\n\n  {\n    const writable = store.createWriteStream(key, value)\n    notEqual(writable, undefined)\n    writeBody(writable, body)\n  }\n})\n\ntest('SqliteCacheStore write & read', { skip: runtimeFeatures.has('sqlite') === false }, async () => {\n  const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')\n\n  const store = new SqliteCacheStore({\n    maxCount: 10\n  })\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}\n   */\n  const key = {\n    origin: 'localhost',\n    path: '/',\n    method: 'GET',\n    headers: {}\n  }\n\n  /**\n   * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: Buffer }}\n   */\n  const value = {\n    statusCode: 200,\n    statusMessage: '',\n    headers: { foo: 'bar' },\n    cacheControlDirectives: { 'max-stale': 0 },\n    cachedAt: Date.now(),\n    staleAt: Date.now() + 10000,\n    deleteAt: Date.now() + 20000,\n    body: Buffer.from('asd'),\n    etag: undefined,\n    vary: undefined\n  }\n\n  store.set(key, value)\n\n  deepStrictEqual(store.get(key), value)\n})\n"
  },
  {
    "path": "test/cache-interceptor/utils.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst { deepStrictEqual, equal } = require('node:assert')\nconst { parseCacheControlHeader, parseVaryHeader, isEtagUsable } = require('../../lib/util/cache')\n\ndescribe('parseCacheControlHeader', () => {\n  test('all directives are parsed properly when in their correct format', () => {\n    const directives = parseCacheControlHeader(\n      'max-stale=1, min-fresh=1, max-age=1, s-maxage=1, stale-while-revalidate=1, stale-if-error=1, public, private, no-store, no-cache, must-revalidate, proxy-revalidate, immutable, no-transform, must-understand, only-if-cached'\n    )\n    deepStrictEqual(directives, {\n      'max-stale': 1,\n      'min-fresh': 1,\n      'max-age': 1,\n      's-maxage': 1,\n      'stale-while-revalidate': 1,\n      'stale-if-error': 1,\n      public: true,\n      private: true,\n      'no-store': true,\n      'no-cache': true,\n      'must-revalidate': true,\n      'proxy-revalidate': true,\n      immutable: true,\n      'no-transform': true,\n      'must-understand': true,\n      'only-if-cached': true\n    })\n  })\n\n  test('handles weird spacings', () => {\n    const directives = parseCacheControlHeader(\n      'max-stale=1, min-fresh=1,     max-age=1,s-maxage=1,  stale-while-revalidate=1,stale-if-error=1,public,private'\n    )\n    deepStrictEqual(directives, {\n      'max-stale': 1,\n      'min-fresh': 1,\n      'max-age': 1,\n      's-maxage': 1,\n      'stale-while-revalidate': 1,\n      'stale-if-error': 1,\n      public: true,\n      private: true\n    })\n  })\n\n  test('unknown directives are ignored', () => {\n    const directives = parseCacheControlHeader('max-age=123, something-else=456')\n    deepStrictEqual(directives, { 'max-age': 123 })\n  })\n\n  test('directives with incorrect types are ignored', () => {\n    const directives = parseCacheControlHeader('max-age=true, only-if-cached=123')\n    deepStrictEqual(directives, {})\n  })\n\n  test('the last instance of a directive takes precedence', () => {\n    const directives = parseCacheControlHeader('max-age=1, max-age=2')\n    deepStrictEqual(directives, { 'max-age': 2 })\n  })\n\n  test('case insensitive', () => {\n    const directives = parseCacheControlHeader('Max-Age=123')\n    deepStrictEqual(directives, { 'max-age': 123 })\n  })\n\n  test('no-cache with headers', () => {\n    let directives = parseCacheControlHeader('max-age=10, no-cache=some-header, only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      'no-cache': [\n        'some-header'\n      ],\n      'only-if-cached': true\n    })\n\n    directives = parseCacheControlHeader('max-age=10, no-cache=\"some-header\", only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      'no-cache': [\n        'some-header'\n      ],\n      'only-if-cached': true\n    })\n\n    directives = parseCacheControlHeader('max-age=10, no-cache=\"some-header, another-one\", only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      'no-cache': [\n        'some-header',\n        'another-one'\n      ],\n      'only-if-cached': true\n    })\n  })\n\n  test('private with headers', () => {\n    let directives = parseCacheControlHeader('max-age=10, private=some-header, only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      private: [\n        'some-header'\n      ],\n      'only-if-cached': true\n    })\n\n    directives = parseCacheControlHeader('max-age=10, private=\"some-header\", only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      private: [\n        'some-header'\n      ],\n      'only-if-cached': true\n    })\n\n    directives = parseCacheControlHeader('max-age=10, private=\"some-header, another-one\", only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      private: [\n        'some-header',\n        'another-one'\n      ],\n      'only-if-cached': true\n    })\n\n    // Missing ending quote, invalid & should be skipped\n    directives = parseCacheControlHeader('max-age=10, private=\"some-header, another-one, only-if-cached')\n    deepStrictEqual(directives, {\n      'max-age': 10,\n      'only-if-cached': true\n    })\n  })\n\n  test('handles multiple headers correctly', () => {\n    // For requests like\n    //  cache-control: max-stale=1\n    //  cache-control: min-fresh-1\n    //  ...\n    const directives = parseCacheControlHeader([\n      'max-stale=1',\n      'min-fresh=1',\n      'max-age=1',\n      's-maxage=1',\n      'stale-while-revalidate=1',\n      'stale-if-error=1',\n      'public',\n      'private',\n      'no-store',\n      'no-cache',\n      'must-revalidate',\n      'proxy-revalidate',\n      'immutable',\n      'no-transform',\n      'must-understand',\n      'only-if-cached'\n    ])\n    deepStrictEqual(directives, {\n      'max-stale': 1,\n      'min-fresh': 1,\n      'max-age': 1,\n      's-maxage': 1,\n      'stale-while-revalidate': 1,\n      'stale-if-error': 1,\n      public: true,\n      private: true,\n      'no-store': true,\n      'no-cache': true,\n      'must-revalidate': true,\n      'proxy-revalidate': true,\n      immutable: true,\n      'no-transform': true,\n      'must-understand': true,\n      'only-if-cached': true\n    })\n  })\n})\n\ndescribe('parseVaryHeader', () => {\n  test('basic usage', () => {\n    const output = parseVaryHeader('some-header, another-one', {\n      'some-header': 'asd',\n      'another-one': '123',\n      'third-header': 'cool'\n    })\n    deepStrictEqual(output, {\n      'some-header': 'asd',\n      'another-one': '123'\n    })\n  })\n\n  test('handles weird spacings', () => {\n    const output = parseVaryHeader('some-header,    another-one,something-else', {\n      'some-header': 'asd',\n      'another-one': '123',\n      'something-else': 'asd123',\n      'third-header': 'cool'\n    })\n    deepStrictEqual(output, {\n      'some-header': 'asd',\n      'another-one': '123',\n      'something-else': 'asd123'\n    })\n  })\n\n  test('handles multiple headers correctly', () => {\n    const output = parseVaryHeader(['some-header', 'another-one'], {\n      'some-header': 'asd',\n      'another-one': '123',\n      'third-header': 'cool'\n    })\n    deepStrictEqual(output, {\n      'some-header': 'asd',\n      'another-one': '123'\n    })\n  })\n\n  test('handles missing headers with null', () => {\n    const result = parseVaryHeader('Accept-Encoding, Authorization', {})\n    deepStrictEqual(result, {\n      'accept-encoding': null,\n      authorization: null\n    })\n  })\n\n  test('handles mix of present and missing headers', () => {\n    const result = parseVaryHeader('Accept-Encoding, Authorization', {\n      authorization: 'example-value'\n    })\n    deepStrictEqual(result, {\n      'accept-encoding': null,\n      authorization: 'example-value'\n    })\n  })\n\n  test('handles array input', () => {\n    const result = parseVaryHeader(['Accept-Encoding', 'Authorization'], {\n      'accept-encoding': 'gzip'\n    })\n    deepStrictEqual(result, {\n      'accept-encoding': 'gzip',\n      authorization: null\n    })\n  })\n\n  test('preserves existing * behavior', () => {\n    const headers = { accept: 'text/html' }\n    const result = parseVaryHeader('*', headers)\n    deepStrictEqual(result, headers)\n  })\n})\n\ndescribe('isEtagUsable', () => {\n  const valuesToTest = {\n    // Invalid etags\n    '': false,\n    asd: false,\n    '\"W/\"asd\"\"': false,\n    '\"\"asd\"\"': false,\n\n    // Valid etags\n    '\"asd\"': true,\n    'W/\"ads\"': true,\n\n    // Spec deviations\n    '\"\"': false,\n    'W/\"\"': false\n  }\n\n  for (const key in valuesToTest) {\n    const expectedValue = valuesToTest[key]\n    test(`\\`${key}\\` = ${expectedValue}`, () => {\n      equal(isEtagUsable(key), expectedValue)\n    })\n  }\n})\n"
  },
  {
    "path": "test/client-connect.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client, errors } = require('..')\nconst http = require('node:http')\nconst EE = require('node:events')\nconst { kBusy } = require('../lib/core/symbols')\n\n// TODO: move to test/node-test/client-connect.js\ntest('connect aborted after connect', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const signal = new EE()\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail()\n  })\n  server.on('connect', (req, c, firstBodyChunk) => {\n    signal.emit('abort')\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    pipelining: 3\n  })\n  after(() => client.close())\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.connect({\n    path: '/',\n    signal,\n    opaque: 'asd',\n    blocking: false\n  }, (err, { opaque }) => {\n    t.strictEqual(opaque, 'asd')\n    t.ok(err instanceof errors.RequestAbortedError)\n  })\n  t.strictEqual(client[kBusy], true)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-head-reset-override.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\n\ntest('override HEAD reset', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const expected = 'testing123'\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.method === 'GET') {\n      res.write(expected)\n    }\n    res.end()\n  }).listen(0)\n\n  after(() => server.close())\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  let done\n  client.on('disconnect', () => {\n    if (!done) {\n      t.fail()\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'HEAD',\n    reset: false\n  }, (err, res) => {\n    t.ifError(err)\n    res.body.resume()\n  })\n\n  client.request({\n    path: '/',\n    method: 'HEAD',\n    reset: false\n  }, (err, res) => {\n    t.ifError(err)\n    res.body.resume()\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    reset: false\n  }, (err, res) => {\n    t.ifError(err)\n    let str = ''\n    res.body.on('data', (data) => {\n      str += data\n    }).on('end', () => {\n      t.strictEqual(str, expected)\n      done = true\n      t.end()\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-idempotent-body.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('idempotent retry', async (t) => {\n  t = tspl(t, { plan: 11 })\n\n  const body = 'world'\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    let buf = ''\n    req.on('data', data => {\n      buf += data\n    }).on('end', () => {\n      t.strictEqual(buf, body)\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n\n    const _err = new Error()\n\n    for (let n = 0; n < 4; ++n) {\n      client.stream({\n        path: '/',\n        method: 'PUT',\n        idempotent: true,\n        blocking: false,\n        body\n      }, () => {\n        throw _err\n      }, (err) => {\n        t.strictEqual(err, _err)\n      })\n    }\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-keep-alive.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { kConnect } = require('../lib/core/symbols')\nconst { createServer } = require('node:net')\nconst http = require('node:http')\nconst FakeTimers = require('@sinonjs/fake-timers')\n\ntest('keep-alive header', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=2s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(() => {\n        t.fail()\n      }, 4e3)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('keep-alive header 0', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install()\n  after(() => clock.uninstall())\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=1s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    keepAliveTimeoutThreshold: 500\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n      })\n      clock.tick(600)\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('keep-alive header 1', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=1s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(() => {\n        t.fail()\n      }, 0)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('keep-alive header no postfix', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=2\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(() => {\n        t.fail()\n      }, 4e3)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('keep-alive not timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    apis: ['setTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeoutasdasd=1s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    keepAliveTimeout: 1e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(t.fail, 3e3)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n      clock.tick(1000)\n    }).resume()\n  })\n\n  await t.completed\n})\n\ntest('keep-alive threshold', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    apis: ['setTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=30s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    keepAliveTimeout: 30e3,\n    keepAliveTimeoutThreshold: 29e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(() => {\n        t.fail()\n      }, 5e3)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n      clock.tick(1000)\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('keep-alive max keepalive', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    apis: ['setTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=30s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    keepAliveTimeout: 30e3,\n    keepAliveMaxTimeout: 1e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      const timeout = setTimeout(() => {\n        t.fail()\n      }, 3e3)\n      client.on('disconnect', () => {\n        t.ok(true, 'pass')\n        clearTimeout(timeout)\n      })\n      clock.tick(1000)\n    }).resume()\n  })\n  await t.completed\n})\n\ntest('connection close', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  let close = false\n  const server = createServer((socket) => {\n    if (close) {\n      return\n    }\n    close = true\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Connection: close\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    pipelining: 2\n  })\n  after(() => client.close())\n\n  client[kConnect](() => {\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.on('end', () => {\n        const timeout = setTimeout(() => {\n          t.fail()\n        }, 3e3)\n        client.once('disconnect', () => {\n          close = false\n          t.ok(true, 'pass')\n          clearTimeout(timeout)\n        })\n      }).resume()\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.on('end', () => {\n        const timeout = setTimeout(() => {\n          t.fail()\n        }, 3e3)\n        client.once('disconnect', () => {\n          t.ok(true, 'pass')\n          clearTimeout(timeout)\n        })\n      }).resume()\n    })\n  })\n  await t.completed\n})\n\ntest('Disable keep alive', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const ports = []\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(ports.includes(req.socket.remotePort), false)\n    ports.push(req.socket.remotePort)\n    t.strictEqual(req.headers.connection, 'close')\n    res.writeHead(200, { connection: 'close' })\n    res.end()\n  })\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, { pipelining: 0 })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, { body }) => {\n    t.ifError(err)\n    body.on('end', () => {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { body }) => {\n        t.ifError(err)\n        body.on('end', () => {\n          t.ok(true, 'pass')\n        }).resume()\n      })\n    }).resume()\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-node-max-header-size.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { once } = require('node:events')\nconst { exec } = require('node:child_process')\nconst { test, before, after, describe } = require('node:test')\nconst { createServer } = require('node:http')\n\ndescribe(\"Node.js' --max-http-header-size cli option\", () => {\n  let server\n\n  before(async () => {\n    server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', {\n        'Content-Length': 2\n      })\n      res.write('OK')\n      res.end()\n    }).listen(0)\n\n    await once(server, 'listening')\n  })\n\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  test(\"respect Node.js' --max-http-header-size\", async (t) => {\n    t = tspl(t, { plan: 6 })\n    const command = 'node --disable-warning=ExperimentalWarning -e \"require(\\'.\\').request(\\'http://localhost:' + server.address().port + '\\')\"'\n\n    exec(`${command} --max-http-header-size=1`, { stdio: 'pipe' }, (err, stdout, stderr) => {\n      t.strictEqual(err.code, 1)\n      t.strictEqual(stdout, '')\n      t.match(stderr, /UND_ERR_HEADERS_OVERFLOW/, '--max-http-header-size=1 should throw')\n    })\n\n    exec(command, { stdio: 'pipe' }, (err, stdout, stderr) => {\n      t.ifError(err)\n      t.strictEqual(stdout, '')\n      // Filter out debugger messages that may appear when running with --inspect\n      const filteredStderr = stderr.replace(/Debugger listening on ws:\\/\\/.*?\\n/g, '')\n        .replace(/For help, see:.*?\\n/g, '')\n        .replace(/Debugger attached\\.\\n/g, '')\n        .replace(/Waiting for the debugger to disconnect\\.\\.\\.\\n/g, '')\n      t.strictEqual(filteredStderr, '', 'default max-http-header-size should not throw')\n    })\n\n    await t.completed\n  })\n\n  test('--max-http-header-size with Client API', async (t) => {\n    t = tspl(t, { plan: 6 })\n    const command = 'node --disable-warning=ExperimentalWarning -e \"new (require(\\'.\\').Client)(new URL(\\'http://localhost:200\\'))\"'\n\n    exec(`${command} --max-http-header-size=0`, { stdio: 'pipe' }, (err, stdout, stderr) => {\n      t.strictEqual(err.code, 1)\n      t.strictEqual(stdout, '')\n      t.match(stderr, /http module not available or http.maxHeaderSize invalid/, '--max-http-header-size=0 should result in an Error when using the Client API')\n    })\n\n    exec(command, { stdio: 'pipe' }, (err, stdout, stderr) => {\n      t.ifError(err)\n      t.strictEqual(stdout, '')\n      // Filter out debugger messages that may appear when running with --inspect\n      const filteredStderr = stderr.replace(/Debugger listening on ws:\\/\\/.*?\\n/g, '')\n        .replace(/For help, see:.*?\\n/g, '')\n        .replace(/Debugger attached\\.\\n/g, '')\n        .replace(/Waiting for the debugger to disconnect\\.\\.\\.\\n/g, '')\n      t.strictEqual(filteredStderr, '', 'default max-http-header-size should not throw')\n    })\n\n    await t.completed\n  })\n})\n"
  },
  {
    "path": "test/client-pipeline.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst EE = require('node:events')\nconst { createServer } = require('node:http')\nconst {\n  pipeline,\n  Readable,\n  Transform,\n  Writable,\n  PassThrough\n} = require('node:stream')\n\ntest('pipeline get', async (t) => {\n  t = tspl(t, { plan: 17 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    t.strictEqual(undefined, req.headers['content-length'])\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    {\n      const bufs = []\n      const signal = new EE()\n      client.pipeline({ signal, path: '/', method: 'GET' }, ({ statusCode, headers, body }) => {\n        t.strictEqual(statusCode, 200)\n        t.strictEqual(headers['content-type'], 'text/plain')\n        t.strictEqual(signal.listenerCount('abort'), 1)\n        return body\n      })\n        .end()\n        .on('data', (buf) => {\n          bufs.push(buf)\n        })\n        .on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n        .on('close', () => {\n          t.strictEqual(signal.listenerCount('abort'), 0)\n        })\n      t.strictEqual(signal.listenerCount('abort'), 1)\n    }\n\n    {\n      const bufs = []\n      client.pipeline({ path: '/', method: 'GET' }, ({ statusCode, headers, body }) => {\n        t.strictEqual(statusCode, 200)\n        t.strictEqual(headers['content-type'], 'text/plain')\n        return body\n      })\n        .end()\n        .on('data', (buf) => {\n          bufs.push(buf)\n        })\n        .on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n    }\n  })\n\n  await t.completed\n})\n\ntest('pipeline echo', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let res = ''\n    const buf1 = Buffer.alloc(1e3).toString()\n    const buf2 = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf1)\n          this.push(buf2)\n          this.push(null)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, ({ body }) => {\n        return pipeline(body, new PassThrough(), () => {})\n      }),\n      new Writable({\n        write (chunk, encoding, callback) {\n          res += chunk.toString()\n          callback()\n        },\n        final (callback) {\n          t.strictEqual(res, buf1 + buf2)\n          callback()\n        }\n      }),\n      (err) => {\n        t.ifError(err)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('pipeline ignore request body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  let done\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    res.end()\n    done()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let res = ''\n    const buf1 = Buffer.alloc(1e3).toString()\n    const buf2 = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf1)\n          this.push(buf2)\n          done = () => this.push(null)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, ({ body }) => {\n        return pipeline(body, new PassThrough(), () => {})\n      }),\n      new Writable({\n        write (chunk, encoding, callback) {\n          res += chunk.toString()\n          callback()\n        },\n        final (callback) {\n          t.strictEqual(res, 'asd')\n          callback()\n        }\n      }),\n      (err) => {\n        t.ifError(err)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('pipeline invalid handler', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const client = new Client('http://localhost:5000')\n  client.pipeline({}, null).on('error', (err) => {\n    t.ok(/handler/.test(err))\n  })\n\n  await t.completed\n})\n\ntest('pipeline invalid handler return after destroy should not error', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    after(() => client.destroy())\n\n    const dup = client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      body.on('error', (err) => {\n        t.strictEqual(err.message, 'asd')\n      })\n      dup.destroy(new Error('asd'))\n      return {}\n    })\n      .on('error', (err) => {\n        t.strictEqual(err.message, 'asd')\n      })\n      .on('close', () => {\n        t.ok(true, 'pass')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline error body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const buf = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, ({ body }) => {\n        const pt = new PassThrough()\n        process.nextTick(() => {\n          pt.destroy(new Error('asd'))\n        })\n        body.on('error', (err) => {\n          t.ok(err)\n        })\n        return pipeline(body, pt, () => {})\n      }),\n      new PassThrough(),\n      (err) => {\n        t.ok(err)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('pipeline destroy body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const buf = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, ({ body }) => {\n        const pt = new PassThrough()\n        process.nextTick(() => {\n          pt.destroy()\n        })\n        body.on('error', (err) => {\n          t.ok(err)\n        })\n        return pipeline(body, pt, () => {})\n      }),\n      new PassThrough(),\n      (err) => {\n        t.ok(err)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('pipeline backpressure', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const buf = Buffer.alloc(1e6).toString()\n    const duplex = client.pipeline({\n      path: '/',\n      method: 'PUT'\n    }, ({ body }) => {\n      const pt = new PassThrough()\n      return pipeline(body, pt, () => {})\n    })\n\n    duplex.end(buf)\n    duplex.on('data', () => {\n      duplex.pause()\n      setImmediate(() => {\n        duplex.resume()\n      })\n    }).on('end', () => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipeline invalid handler return', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      // TODO: Should body cause unhandled exception?\n      body.on('error', () => {})\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.InvalidReturnValueError)\n      })\n      .end()\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      // TODO: Should body cause unhandled exception?\n      body.on('error', () => {})\n      return {}\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.InvalidReturnValueError)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline throw handler', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      // TODO: Should body cause unhandled exception?\n      body.on('error', () => {})\n      throw new Error('asd')\n    })\n      .on('error', (err) => {\n        t.strictEqual(err.message, 'asd')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline destroy and throw handler', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const dup = client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      dup.destroy()\n      // TODO: Should body cause unhandled exception?\n      body.on('error', () => {})\n      throw new Error('asd')\n    })\n      .end()\n      .on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n      .on('close', () => {\n        t.ok(true, 'pass')\n      })\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort res', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  let _res\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    _res = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      setImmediate(() => {\n        body.destroy()\n        _res.write('asdasdadasd')\n        const timeout = setTimeout(() => {\n          t.fail()\n        }, 100)\n        client.on('disconnect', () => {\n          clearTimeout(timeout)\n          t.ok(true, 'pass')\n        })\n      })\n      return body\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort server res', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      t.fail()\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.SocketError)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort duplex', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'PUT'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, () => {\n        t.fail()\n      }).destroy().on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort piped res', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      const pt = new PassThrough()\n      setImmediate(() => {\n        pt.destroy()\n      })\n      return pipeline(body, pt, () => {})\n    })\n      .on('error', (err) => {\n        t.strictEqual(err.code, 'UND_ERR_ABORTED')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort piped res 2', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      const pt = new PassThrough()\n      body.on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n      setImmediate(() => {\n        pt.destroy()\n      })\n      body.pipe(pt)\n      return pt\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort piped res 3', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      const pt = new PassThrough()\n      body.on('error', (err) => {\n        t.strictEqual(err.message, 'asd')\n      })\n      setImmediate(() => {\n        pt.destroy(new Error('asd'))\n      })\n      body.pipe(pt)\n      return pt\n    })\n      .on('error', (err) => {\n        t.strictEqual(err.message, 'asd')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort server res after headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  let _res\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    _res = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, (data) => {\n      _res.destroy()\n      return data.body\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.SocketError)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline w/ write abort server res after headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  let _res\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n    _res = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'PUT'\n    }, (data) => {\n      _res.destroy()\n      return data.body\n    })\n      .on('error', (err) => {\n        t.ok(err instanceof errors.SocketError)\n      })\n      .resume()\n      .write('asd')\n  })\n\n  await t.completed\n})\n\ntest('destroy in push', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  let _res\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    _res = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.pipeline({ path: '/', method: 'GET' }, ({ body }) => {\n      body.once('data', () => {\n        _res.write('asd')\n        body.on('data', (buf) => {\n          body.destroy()\n          _res.end()\n        }).on('error', (err) => {\n          t.ok(err)\n        })\n      })\n      return body\n    }).on('error', (err) => {\n      t.ok(err)\n    }).resume().end()\n\n    client.pipeline({ path: '/', method: 'GET' }, ({ body }) => {\n      let buf = ''\n      body.on('data', (chunk) => {\n        buf = chunk.toString()\n        _res.end()\n      }).on('end', () => {\n        t.strictEqual('asd', buf)\n      })\n      return body\n    }).resume().end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline args validation', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:5000')\n\n  const ret = client.pipeline(null, () => {})\n  ret.on('error', (err) => {\n    t.ok(/opts/.test(err.message))\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  await t.completed\n})\n\ntest('pipeline factory throw not unhandled', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, (data) => {\n      throw new Error('asd')\n    })\n      .on('error', (err) => {\n        t.ok(err)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline destroy before dispatch', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client\n      .pipeline({ path: '/', method: 'GET' }, ({ body }) => {\n        return body\n      })\n      .on('error', (err) => {\n        t.ok(err)\n      })\n      .end()\n      .destroy()\n  })\n\n  await t.completed\n})\n\ntest('pipeline legacy stream', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write(Buffer.alloc(16e3))\n    setImmediate(() => {\n      res.end(Buffer.alloc(16e3))\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client\n      .pipeline({ path: '/', method: 'GET' }, ({ body }) => {\n        const pt = new PassThrough()\n        pt.pause = null\n        return body.pipe(pt)\n      })\n      .resume()\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline objectMode', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify({ asd: 1 }))\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client\n      .pipeline({ path: '/', method: 'GET', objectMode: true }, ({ body }) => {\n        return pipeline(body, new Transform({\n          readableObjectMode: true,\n          transform (chunk, encoding, callback) {\n            callback(null, JSON.parse(chunk))\n          }\n        }), () => {})\n      })\n      .on('data', data => {\n        t.deepStrictEqual(data, { asd: 1 })\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline invalid opts', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify({ asd: 1 }))\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.close((err) => {\n      t.ifError(err)\n    })\n    client\n      .pipeline({ path: '/', method: 'GET', objectMode: true }, ({ body }) => {\n        t.fail()\n      })\n      .on('error', (err) => {\n        t.ok(err)\n      })\n  })\n\n  await t.completed\n})\n\ntest('pipeline CONNECT throw', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'CONNECT'\n    }, () => {\n      t.fail()\n    }).on('error', (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n    })\n    client.on('disconnect', () => {\n      t.fail()\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipeline body without destroy', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => {\n      const pt = new PassThrough({ autoDestroy: false })\n      pt.destroy = null\n      return body.pipe(pt)\n    })\n      .end()\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n      .resume()\n  })\n\n  await t.completed\n})\n\ntest('pipeline ignore 1xx', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let buf = ''\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => body)\n      .on('data', (chunk) => {\n        buf += chunk\n      })\n      .on('end', () => {\n        t.strictEqual(buf, 'hello')\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline ignore 1xx and use onInfo', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const infos = []\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let buf = ''\n    client.pipeline({\n      path: '/',\n      method: 'GET',\n      onInfo: (x) => {\n        infos.push(x)\n      }\n    }, ({ body }) => body)\n      .on('data', (chunk) => {\n        buf += chunk\n      })\n      .on('end', () => {\n        t.strictEqual(buf, 'hello')\n        t.strictEqual(infos.length, 1)\n        t.strictEqual(infos[0].statusCode, 102)\n      })\n      .end()\n  })\n\n  await t.completed\n})\n\ntest('pipeline backpressure', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const expected = Buffer.alloc(1e6).toString()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end(expected)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let buf = ''\n    client.pipeline({\n      path: '/',\n      method: 'GET'\n    }, ({ body }) => body)\n      .end()\n      .pipe(new Transform({\n        highWaterMark: 1,\n        transform (chunk, encoding, callback) {\n          setImmediate(() => {\n            callback(null, chunk)\n          })\n        }\n      }))\n      .on('data', chunk => {\n        buf += chunk\n      })\n      .on('end', () => {\n        t.strictEqual(buf, expected)\n      })\n  })\n\n  await t.completed\n})\n\ntest('pipeline abort after headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.write('asd')\n    setImmediate(() => {\n      res.write('asd')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const signal = new EE()\n    client.pipeline({\n      path: '/',\n      method: 'GET',\n      signal\n    }, ({ body }) => {\n      process.nextTick(() => {\n        signal.emit('abort')\n      })\n      return body\n    })\n      .end()\n      .on('error', (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n      })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-pipelining.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { finished, Readable } = require('node:stream')\nconst { kConnect } = require('../lib/core/symbols')\nconst EE = require('node:events')\nconst { kBusy, kRunning, kSize } = require('../lib/core/symbols')\nconst { maybeWrapStream, consts } = require('./utils/async-iterators')\n\ntest('20 times GET with pipelining 10', async (t) => {\n  const num = 20\n  t = tspl(t, { plan: 3 * num + 1 })\n\n  let count = 0\n  let countGreaterThanOne = false\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    count++\n    setTimeout(function () {\n      countGreaterThanOne = countGreaterThanOne || count > 1\n      res.end(req.url)\n    }, 10)\n  })\n  after(() => server.close())\n\n  // needed to check for a warning on the maxListeners on the socket\n  function onWarning (warning) {\n    if (!/ExperimentalWarning/.test(warning)) {\n      t.fail()\n    }\n  }\n  process.on('warning', onWarning)\n  after(() => {\n    process.removeListener('warning', onWarning)\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 10\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    for (let i = 0; i < num; i++) {\n      makeRequest(i)\n    }\n\n    function makeRequest (i) {\n      makeRequestAndExpectUrl(client, i, t, () => {\n        count--\n\n        if (i === num - 1) {\n          t.ok(countGreaterThanOne, 'seen more than one parallel request')\n        }\n      })\n    }\n  })\n\n  await t.completed\n})\n\nfunction makeRequestAndExpectUrl (client, i, t, cb) {\n  return client.request({ path: '/' + i, method: 'GET', blocking: false }, (err, { statusCode, headers, body }) => {\n    cb()\n    t.ifError(err)\n    t.strictEqual(statusCode, 200)\n    const bufs = []\n    body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    body.on('end', () => {\n      t.strictEqual('/' + i, Buffer.concat(bufs).toString('utf8'))\n    })\n  })\n}\n\ntest('A client should enqueue as much as twice its pipelining factor', async (t) => {\n  const num = 10\n  let sent = 0\n  // x * 6 + 1 t.ok + 5 drain\n  t = tspl(t, { plan: num * 6 + 1 + 5 + 2 })\n\n  let count = 0\n  let countGreaterThanOne = false\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    count++\n    t.ok(count <= 5)\n    setTimeout(function () {\n      countGreaterThanOne = countGreaterThanOne || count > 1\n      res.end(req.url)\n    }, 10)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    for (; sent < 2;) {\n      t.ok(client[kSize] <= client.pipelining, 'client is not full')\n      makeRequest()\n      t.ok(client[kSize] <= client.pipelining, 'we can send more requests')\n    }\n\n    t.ok(client[kBusy], 'client is busy')\n    t.ok(client[kSize] <= client.pipelining, 'client is full')\n    makeRequest()\n    t.ok(client[kBusy], 'we must stop now')\n    t.ok(client[kBusy], 'client is busy')\n    t.ok(client[kSize] > client.pipelining, 'client is full')\n\n    function makeRequest () {\n      makeRequestAndExpectUrl(client, sent++, t, () => {\n        count--\n        setImmediate(() => {\n          if (client[kSize] === 0) {\n            t.ok(countGreaterThanOne, 'seen more than one parallel request')\n            const start = sent\n            for (; sent < start + 2 && sent < num;) {\n              t.ok(client[kSize] <= client.pipelining, 'client is not full')\n              t.ok(makeRequest())\n            }\n          }\n        })\n      })\n      return client[kSize] <= client.pipelining\n    }\n  })\n\n  await t.completed\n})\n\ntest('pipeline 1 is 1 active request', async (t) => {\n  t = tspl(t, { plan: 9 })\n\n  let res2\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    res2 = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1\n    })\n    after(() => client.destroy())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.strictEqual(client[kSize], 1)\n      t.ifError(err)\n      t.strictEqual(client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        finished(data.body, (err) => {\n          t.ok(err)\n          client.close((err) => {\n            t.ifError(err)\n          })\n        })\n        data.body.destroy()\n        res2.end()\n      }), undefined)\n      data.body.resume()\n      res2.end()\n    })\n    t.ok(client[kSize] <= client.pipelining)\n    t.ok(client[kBusy])\n    t.strictEqual(client[kSize], 1)\n  })\n\n  await t.completed\n})\n\ntest('pipelined chunked POST stream', async (t) => {\n  t = tspl(t, { plan: 4 + 8 + 8 })\n\n  let a = 0\n  let b = 0\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', chunk => {\n      // Make sure a and b don't interleave.\n      t.ok(a === 9 || b === 0)\n      res.write(chunk)\n    }).on('end', () => {\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: new Readable({\n        read () {\n          this.push(++a > 8 ? null : 'a')\n        }\n      })\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: new Readable({\n        read () {\n          this.push(++b > 8 ? null : 'b')\n        }\n      })\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipelined chunked POST iterator', async (t) => {\n  t = tspl(t, { plan: 4 + 8 + 8 })\n\n  let a = 0\n  let b = 0\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', chunk => {\n      // Make sure a and b don't interleave.\n      t.ok(a === 9 || b === 0)\n      res.write(chunk)\n    }).on('end', () => {\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: (async function * () {\n        while (++a <= 8) {\n          yield 'a'\n        }\n      })()\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: (async function * () {\n        while (++b <= 8) {\n          yield 'b'\n        }\n      })()\n    }, (err, { body }) => {\n      body.resume()\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\nfunction errordInflightPost (bodyType) {\n  test(`errored POST body lets inflight complete ${bodyType}`, async (t) => {\n    t = tspl(t, { plan: 6 })\n\n    let serverRes\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.on('request', (req, res) => {\n      serverRes = res\n      res.write('asd')\n    })\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        pipelining: 2\n      })\n      after(() => client.destroy())\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .once('data', () => {\n            client.request({\n              path: '/',\n              method: 'POST',\n              opaque: 'asd',\n              body: maybeWrapStream(new Readable({\n                read () {\n                  this.destroy(new Error('kaboom'))\n                }\n              }).once('error', (err) => {\n                t.ok(err)\n              }).on('error', () => {\n                // Readable emits error twice...\n              }), bodyType)\n            }, (err, data) => {\n              t.ok(err)\n              t.strictEqual(data.opaque, 'asd')\n            })\n            client.close((err) => {\n              t.ifError(err)\n            })\n            serverRes.end()\n          })\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n    })\n    await t.completed\n  })\n}\n\nerrordInflightPost(consts.STREAM)\nerrordInflightPost(consts.ASYNC_ITERATOR)\n\ntest('pipelining non-idempotent', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    setTimeout(() => {\n      res.end('asd')\n    }, 10)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    let ended = false\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n          ended = true\n        })\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      idempotent: false\n    }, (err, data) => {\n      t.ifError(err)\n      t.strictEqual(ended, true)\n      data.body.resume()\n    })\n  })\n\n  await t.completed\n})\n\nfunction pipeliningNonIdempotentWithBody (bodyType) {\n  test(`pipelining non-idempotent w body ${bodyType}`, async (t) => {\n    t = tspl(t, { plan: 4 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.on('request', (req, res) => {\n      setImmediate(() => {\n        res.end('asd')\n      })\n    })\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        pipelining: 2\n      })\n      after(() => client.close())\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      let ended = false\n      let reading = false\n      client.request({\n        path: '/',\n        method: 'POST',\n        body: maybeWrapStream(new Readable({\n          read () {\n            if (reading) {\n              return\n            }\n            reading = true\n            this.push('asd')\n            setImmediate(() => {\n              this.push(null)\n              ended = true\n            })\n          }\n        }), bodyType)\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        idempotent: false\n      }, (err, data) => {\n        t.ifError(err)\n        t.strictEqual(ended, true)\n        data.body.resume()\n      })\n    })\n\n    await t.completed\n  })\n}\n\npipeliningNonIdempotentWithBody(consts.STREAM)\npipeliningNonIdempotentWithBody(consts.ASYNC_ITERATOR)\n\nfunction pipeliningHeadBusy (bodyType) {\n  test(`pipelining HEAD busy ${bodyType}`, async (t) => {\n    t = tspl(t, { plan: 7 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.on('request', (req, res) => {\n      res.end('asd')\n    })\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        pipelining: 10\n      })\n      after(() => client.close())\n\n      client[kConnect](() => {\n        let ended = false\n        client.once('disconnect', () => {\n          t.strictEqual(ended, true)\n        })\n\n        {\n          const body = new Readable({\n            read () { }\n          })\n          client.request({\n            path: '/',\n            method: 'GET',\n            body: maybeWrapStream(body, bodyType)\n          }, (err, data) => {\n            t.ifError(err)\n            data.body\n              .resume()\n              .on('end', () => {\n                t.ok(true, 'pass')\n              })\n          })\n          body.push(null)\n          t.strictEqual(client[kBusy], true)\n        }\n\n        {\n          const body = new Readable({\n            read () { }\n          })\n          client.request({\n            path: '/',\n            method: 'HEAD',\n            body: maybeWrapStream(body, bodyType)\n          }, (err, data) => {\n            t.ifError(err)\n            data.body\n              .resume()\n              .on('end', () => {\n                ended = true\n                t.ok(true, 'pass')\n              })\n          })\n          body.push(null)\n          t.strictEqual(client[kBusy], true)\n        }\n      })\n    })\n\n    await t.completed\n  })\n}\n\npipeliningHeadBusy(consts.STREAM)\npipeliningHeadBusy(consts.ASYNC_ITERATOR)\n\ntest('pipelining empty pipeline before reset', async (t) => {\n  t = tspl(t, { plan: 8 })\n\n  let c = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    if (c++ === 0) {\n      res.end('asd')\n    } else {\n      setTimeout(() => {\n        res.end('asd')\n      }, 100)\n    }\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 10\n    })\n    after(() => client.close())\n\n    client[kConnect](() => {\n      let ended = false\n      client.once('disconnect', () => {\n        t.strictEqual(ended, true)\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        blocking: false\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n      t.strictEqual(client[kBusy], false)\n\n      client.request({\n        path: '/',\n        method: 'HEAD',\n        body: 'asd'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .on('end', () => {\n            ended = true\n            t.ok(true, 'pass')\n          })\n      })\n      t.strictEqual(client[kBusy], true)\n      t.strictEqual(client[kRunning], 2)\n    })\n  })\n\n  await t.completed\n})\n\nfunction pipeliningIdempotentBusy (bodyType) {\n  test(`pipelining idempotent busy ${bodyType}`, async (t) => {\n    t = tspl(t, { plan: 12 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.on('request', (req, res) => {\n      res.end('asd')\n    })\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        pipelining: 10\n      })\n      after(() => client.close())\n\n      {\n        const body = new Readable({\n          read () { }\n        })\n        client.request({\n          path: '/',\n          method: 'GET',\n          body: maybeWrapStream(body, bodyType)\n        }, (err, data) => {\n          t.ifError(err)\n          data.body\n            .resume()\n            .on('end', () => {\n              t.ok(true, 'pass')\n            })\n        })\n        body.push(null)\n        t.strictEqual(client[kBusy], true)\n      }\n\n      client[kConnect](() => {\n        {\n          const body = new Readable({\n            read () { }\n          })\n          client.request({\n            path: '/',\n            method: 'GET',\n            body: maybeWrapStream(body, bodyType)\n          }, (err, data) => {\n            t.ifError(err)\n            data.body\n              .resume()\n              .on('end', () => {\n                t.ok(true, 'pass')\n              })\n          })\n          body.push(null)\n          t.strictEqual(client[kBusy], true)\n        }\n\n        {\n          const signal = new EE()\n          const body = new Readable({\n            read () { }\n          })\n          client.request({\n            path: '/',\n            method: 'GET',\n            body: maybeWrapStream(body, bodyType),\n            signal\n          }, (err, data) => {\n            t.ok(err)\n          })\n          t.strictEqual(client[kBusy], true)\n          signal.emit('abort')\n          t.strictEqual(client[kBusy], true)\n        }\n\n        {\n          const body = new Readable({\n            read () { }\n          })\n          client.request({\n            path: '/',\n            method: 'GET',\n            idempotent: false,\n            body: maybeWrapStream(body, bodyType)\n          }, (err, data) => {\n            t.ifError(err)\n            data.body\n              .resume()\n              .on('end', () => {\n                t.ok(true, 'pass')\n              })\n          })\n          body.push(null)\n          t.strictEqual(client[kBusy], true)\n        }\n      })\n    })\n\n    await t.completed\n  })\n}\n\npipeliningIdempotentBusy(consts.STREAM)\npipeliningIdempotentBusy(consts.ASYNC_ITERATOR)\n\ntest('pipelining blocked', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  let blocking = true\n  let count = 0\n\n  server.on('request', (req, res) => {\n    t.ok(!count || !blocking)\n    count++\n    setImmediate(() => {\n      res.end('asd')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 10\n    })\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n    client.request({\n      path: '/',\n      method: 'GET',\n      blocking: true\n    }, (err, data) => {\n      t.ifError(err)\n      blocking = false\n      data.body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-post.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('request post blob', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    t.strictEqual(req.headers['content-type'], 'application/json')\n    let str = ''\n    for await (const chunk of req) {\n      str += chunk\n    }\n    t.strictEqual(str, 'asd')\n    res.end()\n  })\n  after(server.close.bind(server))\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(client.destroy.bind(client))\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    body: new Blob(['asd'], {\n      type: 'application/json'\n    })\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      t.end()\n    })\n  })\n  await t.completed\n})\n\ntest('request post arrayBuffer', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let str = ''\n    for await (const chunk of req) {\n      str += chunk\n    }\n    t.strictEqual(str, 'asd')\n    res.end()\n  })\n\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.destroy())\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const buf = Buffer.from('asd')\n  const dst = new ArrayBuffer(buf.byteLength)\n  buf.copy(new Uint8Array(dst))\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    body: dst\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-reconnect.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst timers = require('../lib/util/timers')\n\ntest('multiple reconnect', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  let n = 0\n  const clock = FakeTimers.install()\n  after(() => clock.uninstall())\n\n  const orgTimers = { ...timers }\n  Object.assign(timers, { setTimeout, clearTimeout })\n  after(() => {\n    Object.assign(timers, orgTimers)\n  })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    n === 0 ? res.destroy() : res.end('ok')\n  })\n  after(() => server.close())\n\n  server.listen(0)\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(client.destroy.bind(client))\n\n  client.request({ path: '/', method: 'GET' }, (err, data) => {\n    t.ok(err)\n    t.strictEqual(err.code, 'UND_ERR_SOCKET')\n  })\n\n  client.request({ path: '/', method: 'GET' }, (err, data) => {\n    t.ifError(err)\n    data.body\n      .resume()\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n  })\n\n  client.on('disconnect', () => {\n    if (++n === 1) {\n      t.ok(true, 'pass')\n    }\n    process.nextTick(() => {\n      clock.tick(1000)\n    })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-request.js",
    "content": "/* globals AbortController */\n\n'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after, describe, before } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst EE = require('node:events')\nconst { kConnect } = require('../lib/core/symbols')\nconst { Readable } = require('node:stream')\nconst net = require('node:net')\nconst { promisify } = require('node:util')\nconst { NotSupportedError, InvalidArgumentError, AbortError } = require('../lib/core/errors')\nconst { parseFormDataString } = require('./utils/formdata')\n\ntest('request dump head', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-length', 5 * 100)\n    res.flushHeaders()\n    res.write('hello'.repeat(100))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    let dumped = false\n    client.on('disconnect', () => {\n      t.strictEqual(dumped, true)\n    })\n    client.request({\n      path: '/',\n      method: 'HEAD'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.dump({ limit: 1 }).then(() => {\n        dumped = true\n        t.ok(true, 'pass')\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('request dump big', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-length', 999999999)\n    while (res.write('asd')) {\n      // Do nothing...\n    }\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    let dumped = false\n    client.on('disconnect', () => {\n      t.strictEqual(dumped, true)\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.on('data', () => t.fail())\n      body.dump().then(() => {\n        dumped = true\n        t.ok(true, 'pass')\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('request dump', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.shouldKeepAlive = false\n    res.setHeader('content-length', 5)\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    let dumped = false\n    client.on('disconnect', () => {\n      t.strictEqual(dumped, true)\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.dump().then(() => {\n        dumped = true\n        t.ok(true, 'pass')\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('request dump with abort signal', async (t) => {\n  t = tspl(t, { plan: 10 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      const ac = new AbortController()\n      body.dump({ signal: ac.signal }).catch((err) => {\n        t.strictEqual(err.name, 'AbortError')\n        t.strictEqual(err.message, 'This operation was aborted')\n        const stackLines = err.stack.split('\\n').map((l) => l.trim())\n\n        t.ok(stackLines[0].startsWith('AbortError: This operation was aborted'))\n        t.ok(stackLines[1].startsWith('at new DOMException'))\n        t.ok(stackLines[2].startsWith('at AbortController.abort'))\n        t.ok(/client-request.js/.test(stackLines[3]))\n        t.ok(stackLines[4].startsWith('at RequestHandler.runInAsyncScope'))\n        t.ok(stackLines[5].startsWith('at RequestHandler.onHeaders'))\n        t.ok(stackLines[6].startsWith('at Request.onHeaders'))\n        server.close()\n      })\n      ac.abort()\n    })\n  })\n\n  await t.completed\n})\n\ntest('request dump with POJO as invalid signal', async (t) => {\n  t = tspl(t, { plan: 9 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.dump({ signal: {} }).catch((err) => {\n        t.strictEqual(err.name, 'InvalidArgumentError')\n        t.strictEqual(err.message, 'signal must be an AbortSignal')\n        const stackLines = err.stack.split('\\n').map((l) => l.trim())\n\n        t.ok(stackLines[0].startsWith('InvalidArgumentError: signal must be an AbortSignal'))\n        t.ok(stackLines[1].startsWith('at BodyReadable.dump'))\n        t.ok(/client-request.js/.test(stackLines[2]))\n        t.ok(stackLines[3].startsWith('at RequestHandler.runInAsyncScope'))\n        t.ok(stackLines[4].startsWith('at RequestHandler.onHeaders'))\n        t.ok(stackLines[5].startsWith('at Request.onHeaders'))\n        server.close()\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('request dump with aborted signal', async (t) => {\n  t = tspl(t, { plan: 8 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      const ac = new AbortController()\n      ac.abort(new AbortError('This operation was with purpose aborted'))\n\n      body.dump({ signal: ac.signal }).catch((err) => {\n        t.strictEqual(err.name, 'AbortError')\n        t.strictEqual(err.message, 'This operation was with purpose aborted')\n        const stackLines = err.stack.split('\\n').map((l) => l.trim())\n\n        t.ok(stackLines[0].startsWith('AbortError: This operation was with purpose aborted'))\n        t.ok(/client-request.js/.test(stackLines[1]))\n        t.ok(stackLines[2].startsWith('at RequestHandler.runInAsyncScope'))\n        t.ok(stackLines[3].startsWith('at RequestHandler.onHeaders'))\n        t.ok(stackLines[4].startsWith('at Request.onHeaders'))\n        server.close()\n      })\n      ac.abort()\n    })\n  })\n\n  await t.completed\n})\n\ntest('request hwm', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      highWaterMark: 1000\n    }, (err, { body }) => {\n      t.ifError(err)\n      t.deepStrictEqual(body.readableHighWaterMark, 1000)\n      body.dump()\n    })\n  })\n\n  await t.completed\n})\n\ntest('request abort before headers', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const signal = new EE()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('hello')\n    signal.emit('abort')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client[kConnect](() => {\n      client.request({\n        path: '/',\n        method: 'GET',\n        signal\n      }, (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n        t.strictEqual(signal.listenerCount('abort'), 0)\n      })\n      t.strictEqual(signal.listenerCount('abort'), 1)\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        signal\n      }, (err) => {\n        t.ok(err instanceof errors.RequestAbortedError)\n        t.strictEqual(signal.listenerCount('abort'), 0)\n      })\n      t.strictEqual(signal.listenerCount('abort'), 2)\n    })\n  })\n\n  await t.completed\n})\n\ntest('request body destroyed on invalid callback', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const body = new Readable({\n      read () { }\n    })\n    try {\n      client.request({\n        path: '/',\n        method: 'GET',\n        body\n      }, null)\n    } catch (err) {\n      t.strictEqual(body.destroyed, true)\n    }\n  })\n\n  await t.completed\n})\n\ntest('trailers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { Trailer: 'Content-MD5' })\n    res.addTrailers({ 'Content-MD5': 'test' })\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const { body, trailers } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    body\n      .on('data', () => t.fail())\n      .on('end', () => {\n        t.deepStrictEqual(trailers, { 'content-md5': 'test' })\n      })\n  })\n\n  await t.completed\n})\n\ntest('destroy socket abruptly', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    const lines = [\n      'HTTP/1.1 200 OK',\n      'Date: Sat, 09 Oct 2010 14:28:02 GMT',\n      'Connection: close',\n      '',\n      'the body'\n    ]\n    socket.end(lines.join('\\r\\n'))\n\n    // Unfortunately calling destroy synchronously might get us flaky results,\n    // therefore we delay it to the next event loop run.\n    setImmediate(socket.destroy.bind(socket))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const { statusCode, body } = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n\n  t.strictEqual(statusCode, 200)\n\n  body.setEncoding('utf8')\n\n  let actual = ''\n\n  for await (const chunk of body) {\n    actual += chunk\n  }\n\n  t.strictEqual(actual, 'the body')\n})\n\ntest('destroy socket abruptly with keep-alive', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    const lines = [\n      'HTTP/1.1 200 OK',\n      'Date: Sat, 09 Oct 2010 14:28:02 GMT',\n      'Connection: keep-alive',\n      'Content-Length: 42',\n      '',\n      'the body'\n    ]\n    socket.end(lines.join('\\r\\n'))\n\n    // Unfortunately calling destroy synchronously might get us flaky results,\n    // therefore we delay it to the next event loop run.\n    setImmediate(socket.destroy.bind(socket))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const { statusCode, body } = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n\n  t.strictEqual(statusCode, 200)\n\n  body.setEncoding('utf8')\n\n  try {\n    /* eslint-disable */\n    for await (const _ of body) {\n      // empty on purpose\n    }\n    /* eslint-enable */\n    t.fail('no error')\n  } catch (err) {\n    t.ok(true, 'error happened')\n  }\n})\n\ntest('request json', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    t.deepStrictEqual(obj, await body.json())\n  })\n\n  await t.completed\n})\n\ntest('request long multibyte json', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: 'あ'.repeat(100000) }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    t.deepStrictEqual(obj, await body.json())\n  })\n\n  await t.completed\n})\n\ntest('request text', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    t.strictEqual(JSON.stringify(obj), await body.text())\n  })\n\n  await t.completed\n})\n\ndescribe('headers', () => {\n  describe('invalid headers', () => {\n    test('invalid header value - array with string with invalid character', async (t) => {\n      t = tspl(t, { plan: 1 })\n\n      const client = new Client('http://localhost:8080')\n      after(() => client.destroy())\n\n      t.rejects(client.request({\n        path: '/',\n        method: 'GET',\n        headers: { name: ['test\\0'] }\n      }), new InvalidArgumentError('invalid name header'))\n\n      await t.completed\n    })\n    test('invalid header value - array with POJO', async (t) => {\n      t = tspl(t, { plan: 1 })\n\n      const client = new Client('http://localhost:8080')\n      after(() => client.destroy())\n\n      t.rejects(client.request({\n        path: '/',\n        method: 'GET',\n        headers: { name: [{}] }\n      }), new InvalidArgumentError('invalid name header'))\n\n      await t.completed\n    })\n\n    test('invalid header value - string with invalid character', async (t) => {\n      t = tspl(t, { plan: 1 })\n\n      const client = new Client('http://localhost:8080')\n      after(() => client.destroy())\n\n      t.rejects(client.request({\n        path: '/',\n        method: 'GET',\n        headers: { name: 'test\\0' }\n      }), new InvalidArgumentError('invalid name header'))\n\n      await t.completed\n    })\n\n    test('invalid header value - object', async (t) => {\n      t = tspl(t, { plan: 1 })\n\n      const client = new Client('http://localhost:8080')\n      after(() => client.destroy())\n\n      t.rejects(client.request({\n        path: '/',\n        method: 'GET',\n        headers: { name: new Date() }\n      }), new InvalidArgumentError('invalid name header'))\n\n      await t.completed\n    })\n  })\n\n  describe('array', () => {\n    let serverAddress\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end(JSON.stringify(req.headers))\n    })\n\n    before(async () => {\n      server.listen(0)\n      await EE.once(server, 'listening')\n      serverAddress = `localhost:${server.address().port}`\n    })\n\n    after(() => {\n      server.closeAllConnections()\n      server.close()\n    })\n\n    test('empty host header', async (t) => {\n      t = tspl(t, { plan: 4 })\n\n      const client = new Client(`http://${serverAddress}`)\n      after(() => client.destroy())\n\n      const testCase = async (expected, actual) => {\n        const { body } = await client.request({\n          path: '/',\n          method: 'GET',\n          headers: expected\n        })\n\n        const result = await body.json()\n        t.deepStrictEqual(result, { ...result, ...actual })\n      }\n\n      await testCase({ key: [null] }, { key: '' })\n      await testCase({ key: ['test'] }, { key: 'test' })\n      await testCase({ key: ['test', 'true'] }, { key: 'test, true' })\n      await testCase({ key: ['test', true] }, { key: 'test, true' })\n\n      await t.completed\n    })\n  })\n\n  describe('host', () => {\n    let serverAddress\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end(req.headers.host)\n    })\n\n    before(async () => {\n      server.listen(0)\n      await EE.once(server, 'listening')\n      serverAddress = `localhost:${server.address().port}`\n    })\n\n    after(() => {\n      server.closeAllConnections()\n      server.close()\n    })\n\n    test('invalid host header', async (t) => {\n      t = tspl(t, { plan: 1 })\n\n      const client = new Client(`http://${serverAddress}`)\n      after(() => client.destroy())\n\n      t.rejects(client.request({\n        path: '/',\n        method: 'GET',\n        headers: {\n          host: [\n            'www.example.com'\n          ]\n        }\n      }), new InvalidArgumentError('invalid host header'))\n\n      await t.completed\n    })\n\n    test('empty host header', async (t) => {\n      t = tspl(t, { plan: 3 })\n\n      const client = new Client(`http://${serverAddress}`)\n      after(() => client.destroy())\n\n      const getWithHost = async (host, wanted) => {\n        const { body } = await client.request({\n          path: '/',\n          method: 'GET',\n          headers: { host }\n        })\n        t.strictEqual(await body.text(), wanted)\n      }\n\n      await getWithHost('test', 'test')\n      await getWithHost(undefined, serverAddress)\n      await getWithHost('', '')\n\n      await t.completed\n    })\n  })\n})\n\ntest('request long multibyte text', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: 'あ'.repeat(100000) }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    t.strictEqual(JSON.stringify(obj), await body.text())\n  })\n\n  await t.completed\n})\n\ntest('request blob', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    const blob = await body.blob()\n    t.deepStrictEqual(obj, JSON.parse(await blob.text()))\n    t.strictEqual(blob.type, 'application/json')\n  })\n\n  await t.completed\n})\n\ntest('request arrayBuffer', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    const ab = await body.arrayBuffer()\n\n    t.deepStrictEqual(Buffer.from(JSON.stringify(obj)), Buffer.from(ab))\n    t.ok(ab instanceof ArrayBuffer)\n  })\n\n  await t.completed\n})\n\ntest('request bytes', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    const bytes = await body.bytes()\n\n    t.deepStrictEqual(new TextEncoder().encode(JSON.stringify(obj)), bytes)\n    t.ok(bytes instanceof Uint8Array)\n  })\n\n  await t.completed\n})\n\ntest('request body', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    let x = ''\n    for await (const chunk of body.body) {\n      x += Buffer.from(chunk)\n    }\n    t.strictEqual(JSON.stringify(obj), x)\n  })\n\n  await t.completed\n})\n\ntest('request post body no missing data', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'asd')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET',\n      body: new Readable({\n        read () {\n          this.push('asd')\n          this.push(null)\n        }\n      })\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body no extra data handler', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'asd')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const reqBody = new Readable({\n      read () {\n        this.push('asd')\n        this.push(null)\n      }\n    })\n    process.nextTick(() => {\n      t.strictEqual(reqBody.listenerCount('data'), 0)\n    })\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET',\n      body: reqBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request with onInfo callback', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const infos = []\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify({ foo: 'bar' }))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    await client.request({\n      path: '/',\n      method: 'GET',\n      onInfo: (x) => { infos.push(x) }\n    })\n    t.strictEqual(infos.length, 1)\n    t.strictEqual(infos[0].statusCode, 102)\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request with onInfo callback but socket is destroyed before end of response', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const infos = []\n  let response\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    response = res\n    res.writeProcessing()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n    try {\n      await client.request({\n        path: '/',\n        method: 'GET',\n        onInfo: (x) => {\n          infos.push(x)\n          response.destroy()\n        }\n      })\n      t.fail()\n    } catch (e) {\n      t.ok(e)\n      t.strictEqual(e.message, 'other side closed')\n    }\n    t.strictEqual(infos.length, 1)\n    t.strictEqual(infos[0].statusCode, 102)\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request onInfo callback headers parsing', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const infos = []\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    const lines = [\n      'HTTP/1.1 103 Early Hints',\n      'Link: </style.css>; rel=preload; as=style',\n      '',\n      'HTTP/1.1 200 OK',\n      'Date: Sat, 09 Oct 2010 14:28:02 GMT',\n      'Connection: close',\n      '',\n      'the body'\n    ]\n    socket.end(lines.join('\\r\\n'))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const { body } = await client.request({\n    path: '/',\n    method: 'GET',\n    onInfo: (x) => { infos.push(x) }\n  })\n  await body.dump()\n  t.strictEqual(infos.length, 1)\n  t.strictEqual(infos[0].statusCode, 103)\n  t.deepStrictEqual(infos[0].headers, { link: '</style.css>; rel=preload; as=style' })\n  t.ok(true, 'pass')\n})\n\ntest('request raw responseHeaders', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const infos = []\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    const lines = [\n      'HTTP/1.1 103 Early Hints',\n      'Link: </style.css>; rel=preload; as=style',\n      '',\n      'HTTP/1.1 200 OK',\n      'Date: Sat, 09 Oct 2010 14:28:02 GMT',\n      'Connection: close',\n      '',\n      'the body'\n    ]\n    socket.end(lines.join('\\r\\n'))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const { body, headers } = await client.request({\n    path: '/',\n    method: 'GET',\n    responseHeaders: 'raw',\n    onInfo: (x) => { infos.push(x) }\n  })\n  await body.dump()\n  t.strictEqual(infos.length, 1)\n  t.deepStrictEqual(infos[0].headers, ['Link', '</style.css>; rel=preload; as=style'])\n  t.deepStrictEqual(headers, ['Date', 'Sat, 09 Oct 2010 14:28:02 GMT', 'Connection', 'close'])\n  t.ok(true, 'pass')\n})\n\ntest('request formData', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    try {\n      await body.formData()\n      t.fail('should throw NotSupportedError')\n    } catch (error) {\n      t.ok(error instanceof NotSupportedError)\n    }\n  })\n\n  await t.completed\n})\n\ntest('request text2', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    const p = body.text()\n    let ret = ''\n    body.on('data', chunk => {\n      ret += chunk\n    }).on('end', () => {\n      t.strictEqual(JSON.stringify(obj), ret)\n    })\n    t.strictEqual(JSON.stringify(obj), await p)\n  })\n\n  await t.completed\n})\n\ntest('request with FormData body', async (t) => {\n  const { FormData } = require('../')\n\n  const fd = new FormData()\n  fd.set('key', 'value')\n  fd.set('file', new Blob(['Hello, world!']), 'hello_world.txt')\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const contentType = req.headers['content-type']\n    // ensure we received a multipart/form-data header\n    t.ok(/^multipart\\/form-data; boundary=-+formdata-undici-0\\d+$/.test(contentType))\n\n    const chunks = []\n\n    for await (const chunk of req) {\n      chunks.push(chunk)\n    }\n\n    const { fileMap, fields } = await parseFormDataString(\n      Buffer.concat(chunks),\n      contentType\n    )\n\n    t.deepStrictEqual(fields[0], { key: 'key', value: 'value' })\n    t.ok(fileMap.has('file'))\n    t.strictEqual(fileMap.get('file').data.toString(), 'Hello, world!')\n    t.deepStrictEqual(fileMap.get('file').info, {\n      filename: 'hello_world.txt',\n      encoding: '7bit',\n      mimeType: 'application/octet-stream'\n    })\n\n    return res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    await client.request({\n      path: '/',\n      method: 'POST',\n      body: fd\n    })\n\n    t.end()\n  })\n\n  await t.completed\n})\n\ntest('request post body Buffer from string', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const requestBody = Buffer.from('abcdefghijklmnopqrstuvwxyz')\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'abcdefghijklmnopqrstuvwxyz')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body Buffer from buffer', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = Buffer.from(fullBuffer.buffer, 8, 16)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body Uint8Array', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = new Uint8Array(fullBuffer.buffer, 8, 16)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body Uint32Array', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = new Uint32Array(fullBuffer.buffer, 8, 4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body Float64Array', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = new Float64Array(fullBuffer.buffer, 8, 2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body BigUint64Array', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = new BigUint64Array(fullBuffer.buffer, 8, 2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request post body DataView', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const fullBuffer = new TextEncoder().encode('abcdefghijklmnopqrstuvwxyz')\n  const requestBody = new DataView(fullBuffer.buffer, 8, 16)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let ret = ''\n    for await (const chunk of req) {\n      ret += chunk\n    }\n    t.strictEqual(ret, 'ijklmnopqrstuvwx')\n    res.end()\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      body: requestBody\n    })\n    await body.text()\n    t.ok(true, 'pass')\n  })\n\n  await t.completed\n})\n\ntest('request multibyte json with setEncoding', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const asd = Buffer.from('あいうえお')\n  const data = JSON.stringify({ asd })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write(data.slice(0, 1))\n    setTimeout(() => {\n      res.write(data.slice(1))\n      res.end()\n    }, 100)\n  })\n  after(server.close.bind(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    body.setEncoding('utf8')\n    t.deepStrictEqual(JSON.parse(data), await body.json())\n  })\n\n  await t.completed\n})\n\ntest('request multibyte text with setEncoding', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const data = Buffer.from('あいうえお')\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write(data.slice(0, 1))\n    setTimeout(() => {\n      res.write(data.slice(1))\n      res.end()\n    }, 100)\n  })\n  after(server.close.bind(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    body.setEncoding('utf8')\n    t.deepStrictEqual(data.toString('utf8'), await body.text())\n  })\n\n  await t.completed\n})\n\ntest('request multibyte text with setEncoding', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const data = Buffer.from('あいうえお')\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write(data.slice(0, 1))\n    setTimeout(() => {\n      res.write(data.slice(1))\n      res.end()\n    }, 100)\n  })\n  after(server.close.bind(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n    body.setEncoding('hex')\n    t.deepStrictEqual(data.toString('hex'), await body.text())\n  })\n\n  await t.completed\n})\n\ntest('#3736 - Aborted Response (without consuming body)', async (t) => {\n  const plan = tspl(t, { plan: 1 })\n\n  const controller = new AbortController()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.writeHead(200, 'ok', {\n        'content-type': 'text/plain'\n      })\n      res.write('hello from server')\n      res.end()\n    }, 100)\n  })\n\n  server.listen(0)\n\n  await EE.once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n\n  after(server.close.bind(server))\n  after(client.destroy.bind(client))\n\n  const { signal } = controller\n  const promise = client.request({\n    path: '/',\n    method: 'GET',\n    signal\n  })\n\n  controller.abort()\n\n  await plan.rejects(promise, { message: 'This operation was aborted' })\n\n  await plan.completed\n})\n"
  },
  {
    "path": "test/client-stream.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst { PassThrough, Writable, Readable } = require('node:stream')\nconst EE = require('node:events')\n\ntest('stream get', async (t) => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const signal = new EE()\n    client.stream({\n      signal,\n      path: '/',\n      method: 'GET',\n      opaque: new PassThrough()\n    }, ({ statusCode, headers, opaque: pt }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      pt.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      pt.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n      return pt\n    }, (err) => {\n      t.strictEqual(signal.listenerCount('abort'), 0)\n      t.ifError(err)\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n  })\n\n  await t.completed\n})\n\ntest('stream promise get', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    await client.stream({\n      path: '/',\n      method: 'GET',\n      opaque: new PassThrough()\n    }, ({ statusCode, headers, opaque: pt }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      pt.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      pt.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n      return pt\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream GET destroy res', async (t) => {\n  t = tspl(t, { plan: 14 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, ({ statusCode, headers }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n\n      const pt = new PassThrough()\n        .on('error', (err) => {\n          t.ok(err)\n        })\n        .on('data', () => {\n          pt.destroy(new Error('kaboom'))\n        })\n\n      return pt\n    }, (err) => {\n      t.ok(err)\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, ({ statusCode, headers }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n\n      let ret = ''\n      const pt = new PassThrough()\n      pt.on('data', chunk => {\n        ret += chunk\n      }).on('end', () => {\n        t.strictEqual(ret, 'hello')\n      })\n\n      return pt\n    }, (err) => {\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream GET remote destroy', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    setImmediate(() => {\n      res.destroy()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      const pt = new PassThrough()\n      pt.on('error', (err) => {\n        t.ok(err)\n      })\n      return pt\n    }, (err) => {\n      t.ok(err)\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      const pt = new PassThrough()\n      pt.on('error', (err) => {\n        t.ok(err)\n      })\n      return pt\n    }).catch((err) => {\n      t.ok(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream response resume back pressure and non standard error', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write(Buffer.alloc(1e3))\n    setImmediate(() => {\n      res.write(Buffer.alloc(1e7))\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const pt = new PassThrough()\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      pt.on('data', () => {\n        pt.emit('error', new Error('kaboom'))\n      }).once('error', (err) => {\n        t.strictEqual(err.message, 'kaboom')\n      })\n      return pt\n    }, (err) => {\n      t.ok(err)\n      t.strictEqual(pt.destroyed, true)\n    })\n\n    client.once('disconnect', (err) => {\n      t.ok(err)\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      const pt = new PassThrough()\n      pt.resume()\n      return pt\n    }, (err) => {\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream waits only for writable side', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(Buffer.alloc(1e3))\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const pt = new PassThrough({ autoDestroy: false })\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => pt, (err) => {\n      t.ifError(err)\n      t.strictEqual(pt.destroyed, false)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream args validation', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const client = new Client('http://localhost:5000')\n  client.stream({\n    path: '/',\n    method: 'GET'\n  }, null, (err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.stream(null, null, (err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  try {\n    client.stream(null, null, 'asd')\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n})\n\ntest('stream args validation promise', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:5000')\n  client.stream({\n    path: '/',\n    method: 'GET'\n  }, null).catch((err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.stream(null, null).catch((err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  await t.completed\n})\n\ntest('stream destroy if not readable', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  const pt = new PassThrough()\n  pt.readable = false\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      return pt\n    }, (err) => {\n      t.ifError(err)\n      t.strictEqual(pt.destroyed, true)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream server side destroy', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      t.fail()\n    }, (err) => {\n      t.ok(err instanceof errors.SocketError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream invalid return', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      return {}\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidReturnValueError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream body without destroy', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      const pt = new PassThrough({ autoDestroy: false })\n      pt.destroy = null\n      pt.resume()\n      return pt\n    }, (err) => {\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream factory abort', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const signal = new EE()\n    client.stream({\n      path: '/',\n      method: 'GET',\n      signal\n    }, () => {\n      signal.emit('abort')\n      return new PassThrough()\n    }, (err) => {\n      t.strictEqual(signal.listenerCount('abort'), 0)\n      t.ok(err instanceof errors.RequestAbortedError)\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n  })\n\n  await t.completed\n})\n\ntest('stream factory throw', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      throw new Error('asd')\n    }, (err) => {\n      t.strictEqual(err.message, 'asd')\n    })\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      throw new Error('asd')\n    }, (err) => {\n      t.strictEqual(err.message, 'asd')\n    })\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      return new PassThrough()\n    }, (err) => {\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream CONNECT throw', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    client.stream({\n      path: '/',\n      method: 'CONNECT'\n    }, () => {\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream abort after complete', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const pt = new PassThrough()\n    const signal = new EE()\n    client.stream({\n      path: '/',\n      method: 'GET',\n      signal\n    }, () => {\n      return pt\n    }, (err) => {\n      t.ifError(err)\n      signal.emit('abort')\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream abort before dispatch', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const pt = new PassThrough()\n    const signal = new EE()\n    client.stream({\n      path: '/',\n      method: 'GET',\n      signal\n    }, () => {\n      return pt\n    }, (err) => {\n      t.ok(err instanceof errors.RequestAbortedError)\n    })\n    signal.emit('abort')\n  })\n\n  await t.completed\n})\n\ntest('trailers', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { Trailer: 'Content-MD5' })\n    res.addTrailers({ 'Content-MD5': 'test' })\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => new PassThrough(), (err, data) => {\n      t.ifError(err)\n      t.deepStrictEqual(data.trailers, { 'content-md5': 'test' })\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream ignore 1xx', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    let buf = ''\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => new Writable({\n      write (chunk, encoding, callback) {\n        buf += chunk\n        callback()\n      }\n    }), (err, data) => {\n      t.ifError(err)\n      t.strictEqual(buf, 'hello')\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream ignore 1xx and use onInfo', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const infos = []\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    let buf = ''\n    client.stream({\n      path: '/',\n      method: 'GET',\n      onInfo: (x) => {\n        infos.push(x)\n      }\n    }, () => new Writable({\n      write (chunk, encoding, callback) {\n        buf += chunk\n        callback()\n      }\n    }), (err, data) => {\n      t.ifError(err)\n      t.strictEqual(buf, 'hello')\n      t.strictEqual(infos.length, 1)\n      t.strictEqual(infos[0].statusCode, 102)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream backpressure', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const expected = Buffer.alloc(1e6).toString()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    res.end(expected)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    let buf = ''\n    client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => new Writable({\n      highWaterMark: 1,\n      write (chunk, encoding, callback) {\n        buf += chunk\n        process.nextTick(callback)\n      }\n    }), (err, data) => {\n      t.ifError(err)\n      t.strictEqual(buf, expected)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream body destroyed on invalid callback', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(client.destroy.bind(client))\n\n    const body = new Readable({\n      read () { }\n    })\n    try {\n      client.stream({\n        path: '/',\n        method: 'GET',\n        body\n      }, () => { }, null)\n    } catch (err) {\n      t.strictEqual(body.destroyed, true)\n    }\n  })\n\n  await t.completed\n})\n\ntest('stream needDrain', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(Buffer.alloc(4096))\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => {\n      client.destroy()\n    })\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const dst = new PassThrough()\n    dst.pause()\n\n    if (dst.writableNeedDrain === undefined) {\n      Object.defineProperty(dst, 'writableNeedDrain', {\n        get () {\n          return this._writableState.needDrain\n        }\n      })\n    }\n\n    while (dst.write(Buffer.alloc(4096))) {\n      // Do nothing.\n    }\n\n    const orgWrite = dst.write\n    dst.write = () => t.fail()\n    const p = client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      t.strictEqual(dst._writableState.needDrain, true)\n      t.strictEqual(dst.writableNeedDrain, true)\n\n      setImmediate(() => {\n        dst.write = (...args) => {\n          orgWrite.call(dst, ...args)\n        }\n        dst.resume()\n      })\n\n      return dst\n    })\n\n    p.then(() => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream legacy needDrain', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(Buffer.alloc(4096))\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => {\n      client.destroy()\n    })\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const dst = new PassThrough()\n    dst.pause()\n\n    if (dst.writableNeedDrain !== undefined) {\n      Object.defineProperty(dst, 'writableNeedDrain', {\n        get () {\n        }\n      })\n    }\n\n    while (dst.write(Buffer.alloc(4096))) {\n      // Do nothing\n    }\n\n    const orgWrite = dst.write\n    dst.write = () => t.fail()\n    const p = client.stream({\n      path: '/',\n      method: 'GET'\n    }, () => {\n      t.strictEqual(dst._writableState.needDrain, true)\n      t.strictEqual(dst.writableNeedDrain, undefined)\n\n      setImmediate(() => {\n        dst.write = (...args) => {\n          orgWrite.call(dst, ...args)\n        }\n        dst.resume()\n      })\n\n      return dst\n    })\n\n    p.then(() => {\n      t.ok(true, 'pass')\n    })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-timeout.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst timers = require('../lib/util/timers')\n\ntest('refresh timeout on pause', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.flushHeaders()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 500\n    })\n    after(() => client.destroy())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers, resume) {\n        setTimeout(() => {\n          resume()\n        }, 1000)\n        return false\n      },\n      onData () {\n\n      },\n      onComplete () {\n\n      },\n      onError (err) {\n        t.ok(err instanceof errors.BodyTimeoutError)\n      }\n    })\n  })\n\n  await t.completed\n})\n\ntest('start headers timeout after request body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({ shouldClearNativeTimers: true })\n  after(() => clock.uninstall())\n\n  const orgTimers = { ...timers }\n  Object.assign(timers, { setTimeout, clearTimeout })\n  after(() => {\n    Object.assign(timers, orgTimers)\n  })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0,\n      headersTimeout: 100\n    })\n    after(() => client.destroy())\n\n    const body = new Readable({ read () {} })\n    client.dispatch({\n      path: '/',\n      body,\n      method: 'GET'\n    }, {\n      onConnect () {\n        process.nextTick(() => {\n          clock.tick(200)\n        })\n        queueMicrotask(() => {\n          body.push(null)\n          body.on('end', () => {\n            clock.tick(200)\n          })\n        })\n      },\n      onHeaders (statusCode, headers, resume) {\n      },\n      onData () {\n\n      },\n      onComplete () {\n\n      },\n      onError (err) {\n        t.equal(body.readableEnded, true)\n        t.ok(err instanceof errors.HeadersTimeoutError)\n      }\n    })\n  })\n\n  await t.completed\n})\n\ntest('start headers timeout after async iterator request body', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({ shouldClearNativeTimers: true })\n  after(() => clock.uninstall())\n\n  const orgTimers = { ...timers }\n  Object.assign(timers, { setTimeout, clearTimeout })\n  after(() => {\n    Object.assign(timers, orgTimers)\n  })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0,\n      headersTimeout: 100\n    })\n    after(() => client.destroy())\n    let res\n    const body = (async function * () {\n      await new Promise((resolve) => { res = resolve })\n      process.nextTick(() => {\n        clock.tick(200)\n      })\n    })()\n    client.dispatch({\n      path: '/',\n      body,\n      method: 'GET'\n    }, {\n      onConnect () {\n        process.nextTick(() => {\n          clock.tick(200)\n        })\n        queueMicrotask(() => {\n          res()\n        })\n      },\n      onHeaders (statusCode, headers, resume) {\n      },\n      onData () {\n\n      },\n      onComplete () {\n\n      },\n      onError (err) {\n        t.ok(err instanceof errors.HeadersTimeoutError)\n      }\n    })\n  })\n\n  await t.completed\n})\n\ntest('parser resume with no body timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers, resume) {\n        setTimeout(resume, 2000)\n        return false\n      },\n      onData () {\n\n      },\n      onComplete () {\n        t.ok(true, 'pass')\n      },\n      onError (err) {\n        t.ifError(err)\n      }\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-unref.js",
    "content": "'use strict'\n\nconst { Worker, isMainThread, workerData } = require('node:worker_threads')\n\nif (isMainThread) {\n  const { tspl } = require('@matteo.collina/tspl')\n  const { test, after } = require('node:test')\n  const { once } = require('node:events')\n  const { createServer } = require('node:http')\n\n  test('client automatically closes itself when idle', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(server.close.bind(server))\n    server.keepAliveTimeout = 9999\n\n    server.listen(0)\n\n    await once(server, 'listening')\n    const url = `http://localhost:${server.address().port}`\n    const worker = new Worker(__filename, { workerData: { url } })\n    worker.on('exit', code => {\n      t.strictEqual(code, 0)\n    })\n    await t.completed\n  })\n\n  test('client automatically closes itself if the server is not there', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const url = 'http://localhost:4242' // hopefully empty port\n    const worker = new Worker(__filename, { workerData: { url } })\n    worker.on('exit', code => {\n      t.strictEqual(code, 0)\n    })\n\n    await t.completed\n  })\n} else {\n  const { Client } = require('..')\n\n  const client = new Client(workerData.url)\n  client.request({ path: '/', method: 'GET' }, () => {\n    // We do not care about Errors\n\n    setTimeout(() => {\n      throw new Error()\n    }, 1e3).unref()\n  })\n}\n"
  },
  {
    "path": "test/client-upgrade.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst net = require('node:net')\nconst http = require('node:http')\nconst EE = require('node:events')\nconst { kBusy } = require('../lib/core/symbols')\n\ntest('basic upgrade', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      t.ok(/upgrade: websocket/i.test(d))\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('upgrade: websocket\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n\n    c.on('end', () => {\n      c.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const signal = new EE()\n    client.upgrade({\n      signal,\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket'\n    }, (err, data) => {\n      t.ifError(err)\n\n      t.strictEqual(signal.listenerCount('abort'), 0)\n\n      const { headers, socket } = data\n\n      let recvData = ''\n      data.socket.on('data', (d) => {\n        recvData += d\n      })\n\n      socket.on('close', () => {\n        t.strictEqual(recvData.toString(), 'Body')\n      })\n\n      t.deepStrictEqual(headers, {\n        hello: 'world',\n        connection: 'upgrade',\n        upgrade: 'websocket'\n      })\n      socket.end()\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n  })\n\n  await t.completed\n})\n\ntest('basic upgrade promise', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('upgrade: websocket\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n\n    c.on('end', () => {\n      c.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const { headers, socket } = await client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket'\n    })\n\n    let recvData = ''\n    socket.on('data', (d) => {\n      recvData += d\n    })\n\n    socket.on('close', () => {\n      t.strictEqual(recvData.toString(), 'Body')\n    })\n\n    t.deepStrictEqual(headers, {\n      hello: 'world',\n      connection: 'upgrade',\n      upgrade: 'websocket'\n    })\n    socket.end()\n  })\n\n  await t.completed\n})\n\ntest('upgrade error', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n    c.on('error', () => {\n      // Whether we get an error, end or close is undefined.\n      // Ignore error.\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'Websocket'\n      })\n    } catch (err) {\n      t.ok(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('upgrade invalid opts', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const client = new Client('http://localhost:5432')\n\n  client.upgrade(null, err => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n    t.strictEqual(err.message, 'invalid opts')\n  })\n\n  try {\n    client.upgrade(null, null)\n    t.fail()\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n    t.strictEqual(err.message, 'invalid opts')\n  }\n\n  try {\n    client.upgrade({ path: '/' }, null)\n    t.fail()\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n    t.strictEqual(err.message, 'invalid callback')\n  }\n})\n\ntest('basic upgrade2', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true })\n  server.on('upgrade', (req, c, head) => {\n    c.write('HTTP/1.1 101\\r\\n')\n    c.write('hello: world\\r\\n')\n    c.write('connection: upgrade\\r\\n')\n    c.write('upgrade: websocket\\r\\n')\n    c.write('\\r\\n')\n    c.write('Body')\n    c.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket'\n    }, (err, data) => {\n      t.ifError(err)\n\n      const { headers, socket } = data\n\n      let recvData = ''\n      data.socket.on('data', (d) => {\n        recvData += d\n      })\n\n      socket.on('close', () => {\n        t.strictEqual(recvData.toString(), 'Body')\n      })\n\n      t.deepStrictEqual(headers, {\n        hello: 'world',\n        connection: 'upgrade',\n        upgrade: 'websocket'\n      })\n      socket.end()\n    })\n  })\n\n  await t.completed\n})\n\ntest('upgrade wait for empty pipeline', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  let canConnect = false\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n    canConnect = true\n  })\n  server.on('upgrade', (req, c, firstBodyChunk) => {\n    t.strictEqual(canConnect, true)\n    c.write('HTTP/1.1 101\\r\\n')\n    c.write('hello: world\\r\\n')\n    c.write('connection: upgrade\\r\\n')\n    c.write('upgrade: websocket\\r\\n')\n    c.write('\\r\\n')\n    c.write('Body')\n    c.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      blocking: false\n    }, (err) => {\n      t.ifError(err)\n    })\n    client.once('connect', () => {\n      process.nextTick(() => {\n        t.strictEqual(client[kBusy], false)\n\n        client.upgrade({\n          path: '/'\n        }, (err, { socket }) => {\n          t.ifError(err)\n          let recvData = ''\n          socket.on('data', (d) => {\n            recvData += d\n          })\n\n          socket.on('end', () => {\n            t.strictEqual(recvData.toString(), 'Body')\n          })\n\n          socket.write('Body')\n          socket.end()\n        })\n        t.strictEqual(client[kBusy], true)\n\n        client.request({\n          path: '/',\n          method: 'GET'\n        }, (err) => {\n          t.ifError(err)\n        })\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('upgrade aborted', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail()\n  })\n  server.on('upgrade', (req, c, firstBodyChunk) => {\n    t.fail()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    after(() => client.destroy())\n\n    const signal = new EE()\n    client.upgrade({\n      path: '/',\n      signal,\n      opaque: 'asd'\n    }, (err, { opaque }) => {\n      t.strictEqual(opaque, 'asd')\n      t.ok(err instanceof errors.RequestAbortedError)\n      t.strictEqual(signal.listenerCount('abort'), 0)\n    })\n    t.strictEqual(client[kBusy], true)\n    t.strictEqual(signal.listenerCount('abort'), 1)\n    signal.emit('abort')\n\n    client.close(() => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic aborted after res', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const signal = new EE()\n  const server = http.createServer({ joinDuplicateHeaders: true })\n  server.on('upgrade', (req, c, head) => {\n    c.write('HTTP/1.1 101\\r\\n')\n    c.write('hello: world\\r\\n')\n    c.write('connection: upgrade\\r\\n')\n    c.write('upgrade: websocket\\r\\n')\n    c.write('\\r\\n')\n    c.write('Body')\n    c.end()\n    c.on('error', () => {\n\n    })\n    signal.emit('abort')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket',\n      signal\n    }, (err) => {\n      t.ok(err instanceof errors.RequestAbortedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic upgrade error', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('upgrade: websocket\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n    c.on('error', () => {\n\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const _err = new Error()\n    client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket'\n    }, (err, data) => {\n      t.ifError(err)\n      data.socket.on('error', (err) => {\n        t.strictEqual(err, _err)\n      })\n      throw _err\n    })\n  })\n\n  await t.completed\n})\n\ntest('upgrade disconnect', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = net.createServer(connection => {\n    connection.destroy()\n  })\n\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', (origin, [self], error) => {\n      t.strictEqual(client, self)\n      t.ok(error instanceof Error)\n    })\n\n    client\n      .upgrade({ path: '/', method: 'GET' })\n      .then(() => {\n        t.fail()\n      })\n      .catch(error => {\n        t.ok(error instanceof Error)\n      })\n  })\n\n  await t.completed\n})\n\ntest('upgrade invalid signal', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, () => {\n    t.fail()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      t.fail()\n    })\n\n    client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket',\n      signal: 'error',\n      opaque: 'asd'\n    }, (err, { opaque }) => {\n      t.strictEqual(opaque, 'asd')\n      t.ok(err instanceof errors.InvalidArgumentError)\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client-wasm.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test } = require('node:test')\n\n  ;[\n  ['generic', require('../lib/llhttp/llhttp-wasm.js')],\n  ['simd', require('../lib/llhttp/llhttp_simd-wasm.js')]\n].forEach(([name, llhttp]) => {\n  describe(name, () => {\n    test('can compile the wasm code', async () => {\n      await WebAssembly.compile(llhttp)\n    })\n\n    test('can instantiate the wasm code', async () => {\n      const mod = await WebAssembly.compile(llhttp)\n      await WebAssembly.instantiate(mod, {\n        env: {\n          wasm_on_url: () => { },\n          wasm_on_status: () => { },\n          wasm_on_message_begin: () => { },\n          wasm_on_header_field: () => { },\n          wasm_on_header_value: () => { },\n          wasm_on_headers_complete: () => { },\n          wasm_on_body: () => { },\n          wasm_on_message_complete: () => { }\n        }\n      })\n    })\n\n    describe('exports', async () => {\n      const mod = await WebAssembly.compile(llhttp)\n      const instance = await WebAssembly.instantiate(mod, {\n        env: {\n          wasm_on_url: () => { },\n          wasm_on_status: () => { },\n          wasm_on_message_begin: () => { },\n          wasm_on_header_field: () => { },\n          wasm_on_header_value: () => { },\n          wasm_on_headers_complete: () => { },\n          wasm_on_body: () => { },\n          wasm_on_message_complete: () => { }\n        }\n      })\n\n      test('has the required exports', async (t) => {\n        const requiredExports = [\n          'memory',\n          '_initialize',\n          '__indirect_function_table',\n          'llhttp_init',\n          'llhttp_should_keep_alive',\n          'llhttp_alloc',\n          'malloc',\n          'llhttp_free',\n          'free',\n          'llhttp_get_type',\n          'llhttp_get_http_major',\n          'llhttp_get_http_minor',\n          'llhttp_get_method',\n          'llhttp_get_status_code',\n          'llhttp_get_upgrade',\n          'llhttp_reset',\n          'llhttp_execute',\n          'llhttp_settings_init',\n          'llhttp_finish',\n          'llhttp_pause',\n          'llhttp_resume',\n          'llhttp_resume_after_upgrade',\n          'llhttp_get_errno',\n          'llhttp_get_error_reason',\n          'llhttp_set_error_reason',\n          'llhttp_get_error_pos',\n          'llhttp_errno_name',\n          'llhttp_method_name',\n          'llhttp_status_name',\n          'llhttp_set_lenient_headers',\n          'llhttp_set_lenient_chunked_length',\n          'llhttp_set_lenient_keep_alive',\n          'llhttp_set_lenient_transfer_encoding',\n          'llhttp_set_lenient_version',\n          'llhttp_set_lenient_data_after_close',\n          'llhttp_set_lenient_optional_lf_after_cr',\n          'llhttp_set_lenient_optional_crlf_after_chunk',\n          'llhttp_set_lenient_optional_cr_before_lf',\n          'llhttp_set_lenient_spaces_after_chunk_size',\n          'llhttp_message_needs_eof'\n        ]\n        t = tspl(t, { plan: requiredExports.length })\n\n        for (const key of requiredExports) {\n          t.ok(key in instance.exports, `${key} is exported`)\n        }\n        await t.completed\n      })\n\n      test('instance.exports.memory', async (t) => {\n        t = tspl(t, { plan: 1 })\n\n        t.ok(instance.exports.memory instanceof WebAssembly.Memory, 'memory is present')\n      })\n\n      // _initialize\n      test('instance.exports._initialize', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports._initialize === 'function', '_initialize is present')\n        t.strictEqual(instance.exports._initialize.length, 0, '_initialize has the right number of arguments')\n      })\n\n      // __indirect_function_table\n      test('instance.exports.__indirect_function_table', async (t) => {\n        t = tspl(t, { plan: 1 })\n\n        t.ok(instance.exports.__indirect_function_table instanceof WebAssembly.Table, '__indirect_function_table is present')\n      })\n\n      // malloc\n      test('instance.exports.malloc', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.malloc === 'function', 'malloc is present')\n        t.strictEqual(instance.exports.malloc.length, 1, 'malloc has the right number of arguments')\n      })\n\n      // free\n      test('instance.exports.free', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.free === 'function', 'free is present')\n        t.strictEqual(instance.exports.free.length, 1, 'free has the right number of arguments')\n      })\n\n      // llhttp_init\n      test('instance.exports.llhttp_init', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_init === 'function', 'llhttp_init is present')\n        t.strictEqual(instance.exports.llhttp_init.length, 3, 'llhttp_init has the right number of arguments')\n      })\n\n      // llhttp_alloc\n      test('instance.exports.llhttp_alloc', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_alloc === 'function', 'llhttp_alloc is present')\n        t.strictEqual(instance.exports.llhttp_alloc.length, 1, 'llhttp_alloc has the right number of arguments')\n      })\n\n      // llhttp_free\n      test('instance.exports.llhttp_free', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_free === 'function', 'llhttp_free is present')\n        t.strictEqual(instance.exports.llhttp_free.length, 1, 'llhttp_free has the right number of arguments')\n      })\n\n      // llhttp_get_type\n      test('instance.exports.llhttp_get_type', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_type === 'function', 'llhttp_get_type is present')\n        t.strictEqual(instance.exports.llhttp_get_type.length, 1, 'llhttp_get_type has the right number of arguments')\n      })\n\n      // llhttp_should_keep_alive\n      test('instance.exports.llhttp_should_keep_alive', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_should_keep_alive === 'function', 'llhttp_should_keep_alive is present')\n        t.strictEqual(instance.exports.llhttp_should_keep_alive.length, 1, 'llhttp_should_keep_alive has the right number of arguments')\n      })\n\n      // llhttp_get_http_major\n      test('instance.exports.llhttp_get_http_major', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_http_major === 'function', 'llhttp_get_http_major is present')\n        t.strictEqual(instance.exports.llhttp_get_http_major.length, 1, 'llhttp_get_http_major has the right number of arguments')\n      })\n\n      // llhttp_get_http_minor\n      test('instance.exports.llhttp_get_http_minor', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_http_minor === 'function', 'llhttp_get_http_minor is present')\n        t.strictEqual(instance.exports.llhttp_get_http_minor.length, 1, 'llhttp_get_http_minor has the right number of arguments')\n      })\n\n      // llhttp_get_method\n      test('instance.exports.llhttp_get_method', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_method === 'function', 'llhttp_get_method is present')\n        t.strictEqual(instance.exports.llhttp_get_method.length, 1, 'llhttp_get_method has the right number of arguments')\n      })\n\n      // llhttp_get_status_code\n      test('instance.exports.llhttp_get_status_code', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_status_code === 'function', 'llhttp_get_status_code is present')\n        t.strictEqual(instance.exports.llhttp_get_status_code.length, 1, 'llhttp_get_status_code has the right number of arguments')\n      })\n\n      // llhttp_get_upgrade\n      test('instance.exports.llhttp_get_upgrade', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_upgrade === 'function', 'llhttp_get_upgrade is present')\n        t.strictEqual(instance.exports.llhttp_get_upgrade.length, 1, 'llhttp_get_upgrade has the right number of arguments')\n      })\n\n      // llhttp_reset\n      test('instance.exports.llhttp_reset', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_reset === 'function', 'llhttp_reset is present')\n        t.strictEqual(instance.exports.llhttp_reset.length, 1, 'llhttp_reset has the right number of arguments')\n      })\n\n      // llhttp_execute\n      test('instance.exports.llhttp_execute', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_execute === 'function', 'llhttp_execute is present')\n        t.strictEqual(instance.exports.llhttp_execute.length, 3, 'llhttp_execute has the right number of arguments')\n      })\n\n      // llhttp_settings_init\n      test('instance.exports.llhttp_settings_init', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_settings_init === 'function', 'llhttp_settings_init is present')\n        t.strictEqual(instance.exports.llhttp_settings_init.length, 1, 'llhttp_settings_init has the right number of arguments')\n      })\n\n      // llhttp_finish\n      test('instance.exports.llhttp_finish', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_finish === 'function', 'llhttp_finish is present')\n        t.strictEqual(instance.exports.llhttp_finish.length, 1, 'llhttp_finish has the right number of arguments')\n      })\n\n      // llhttp_pause\n      test('instance.exports.llhttp_pause', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_pause === 'function', 'llhttp_pause is present')\n        t.strictEqual(instance.exports.llhttp_pause.length, 1, 'llhttp_pause has the right number of arguments')\n      })\n\n      // llhttp_resume\n      test('instance.exports.llhttp_resume', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_resume === 'function', 'llhttp_resume is present')\n        t.strictEqual(instance.exports.llhttp_resume.length, 1, 'llhttp_resume has the right number of arguments')\n      })\n\n      // llhttp_resume_after_upgrade\n      test('instance.exports.llhttp_resume_after_upgrade', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_resume_after_upgrade === 'function', 'llhttp_resume_after_upgrade is present')\n        t.strictEqual(instance.exports.llhttp_resume_after_upgrade.length, 1, 'llhttp_resume_after_upgrade has the right number of arguments')\n      })\n\n      // llhttp_get_errno\n      test('instance.exports.llhttp_get_errno', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_errno === 'function', 'llhttp_get_errno is present')\n        t.strictEqual(instance.exports.llhttp_get_errno.length, 1, 'llhttp_get_errno has the right number of arguments')\n      })\n\n      // llhttp_get_error_reason\n      test('instance.exports.llhttp_get_error_reason', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_error_reason === 'function', 'llhttp_get_error_reason is present')\n        t.strictEqual(instance.exports.llhttp_get_error_reason.length, 1, 'llhttp_get_error_reason has the right number of arguments')\n      })\n\n      // llhttp_set_error_reason\n      test('instance.exports.llhttp_set_error_reason', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_set_error_reason === 'function', 'llhttp_set_error_reason is present')\n        t.strictEqual(instance.exports.llhttp_set_error_reason.length, 2, 'llhttp_set_error_reason has the right number of arguments')\n      })\n\n      // llhttp_get_error_pos\n      test('instance.exports.llhttp_get_error_pos', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_get_error_pos === 'function', 'llhttp_get_error_pos is present')\n        t.strictEqual(instance.exports.llhttp_get_error_pos.length, 1, 'llhttp_get_error_pos has the right number of arguments')\n      })\n\n      // llhttp_errno_name\n      test('instance.exports.llhttp_errno_name', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_errno_name === 'function', 'llhttp_errno_name is present')\n        t.strictEqual(instance.exports.llhttp_errno_name.length, 1, 'llhttp_errno_name has the right number of arguments')\n      })\n\n      // llhttp_method_name\n      test('instance.exports.llhttp_method_name', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_method_name === 'function', 'llhttp_method_name is present')\n        t.strictEqual(instance.exports.llhttp_method_name.length, 1, 'llhttp_method_name has the right number of arguments')\n      })\n\n      // llhttp_status_name\n      test('instance.exports.llhttp_status_name', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_status_name === 'function', 'llhttp_status_name is present')\n        t.strictEqual(instance.exports.llhttp_status_name.length, 1, 'llhttp_status_name has the right number of arguments')\n      })\n\n      // llhttp_set_lenient_headers\n      test('instance.exports.llhttp_set_lenient_headers', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_set_lenient_headers === 'function', 'llhttp_set_lenient_headers is present')\n        t.strictEqual(instance.exports.llhttp_set_lenient_headers.length, 2, 'llhttp_set_lenient_headers has the right number of arguments')\n      })\n\n      // llhttp_set_lenient_chunked_length\n      test('instance.exports.llhttp_set_lenient_chunked_length', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_set_lenient_chunked_length === 'function', 'llhttp_set_lenient_chunked_length is present')\n        t.strictEqual(instance.exports.llhttp_set_lenient_chunked_length.length, 2, 'llhttp_set_lenient_chunked_length has the right number of arguments')\n      })\n\n      // llhttp_set_lenient_keep_alive\n      test('instance.exports.llhttp_set_lenient_keep_alive', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_set_lenient_keep_alive === 'function', 'llhttp_set_lenient_keep_alive is present')\n        t.strictEqual(instance.exports.llhttp_set_lenient_keep_alive.length, 2, 'llhttp_set_lenient_keep_alive has the right number of arguments')\n      })\n\n      // llhttp_set_lenient_transfer_encoding\n      test('instance.exports.llhttp_set_lenient_transfer_encoding', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_set_lenient_transfer_encoding === 'function', 'llhttp_set_lenient_transfer_encoding is present')\n        t.strictEqual(instance.exports.llhttp_set_lenient_transfer_encoding.length, 2, 'llhttp_set_lenient_transfer_encoding has the right number of arguments')\n      })\n\n      // llhttp_message_needs_eof\n      test('instance.exports.llhttp_message_needs_eof', async (t) => {\n        t = tspl(t, { plan: 2 })\n\n        t.ok(typeof instance.exports.llhttp_message_needs_eof === 'function', 'llhttp_message_needs_eof is present')\n        t.strictEqual(instance.exports.llhttp_message_needs_eof.length, 1, 'llhttp_message_needs_eof has the right number of arguments')\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/client-write-max-listeners.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\n\ntest('socket close listener does not leak', async (t) => {\n  t = tspl(t, { plan: 32 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const makeBody = () => {\n    return new Readable({\n      read () {\n        process.nextTick(() => {\n          this.push(null)\n        })\n      }\n    })\n  }\n\n  const onRequest = (err, data) => {\n    t.ifError(err)\n    data.body.on('end', () => t.ok(true, 'pass')).resume()\n  }\n\n  function onWarning (warning) {\n    if (!/ExperimentalWarning/.test(warning)) {\n      t.fail()\n    }\n  }\n  process.on('warning', onWarning)\n  after(() => {\n    process.removeListener('warning', onWarning)\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.destroy())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  for (let n = 0; n < 16; ++n) {\n    client.request({ path: '/', method: 'GET', body: makeBody() }, onRequest)\n  }\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/client.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { readFileSync, createReadStream } = require('node:fs')\nconst { createServer } = require('node:http')\nconst { Readable, PassThrough } = require('node:stream')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { kSocket } = require('../lib/core/symbols')\nconst { wrapWithAsyncIterable } = require('./utils/async-iterators')\nconst EE = require('node:events')\nconst { kUrl, kSize, kConnect, kBusy, kConnected, kRunning } = require('../lib/core/symbols')\n\nconst hasIPv6 = (() => {\n  const iFaces = require('node:os').networkInterfaces()\n  const re = process.platform === 'win32' ? /Loopback Pseudo-Interface/ : /lo/\n  return Object.keys(iFaces).some(\n    (name) => re.test(name) && iFaces[name].some(({ family }) => family === 'IPv6')\n  )\n})()\n\ntest('basic get', async (t) => {\n  t = tspl(t, { plan: 24 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    t.strictEqual(undefined, req.headers.foo)\n    t.strictEqual('bar', req.headers.bar)\n    t.strictEqual(undefined, req.headers['content-length'])\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    t.strictEqual(client[kUrl].origin, `http://localhost:${server.address().port}`)\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/',\n      method: 'GET',\n      headers: reqHeaders\n    }, (err, data) => {\n      t.ifError(err)\n      const { statusCode, headers, body } = data\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(signal.listenerCount('abort'), 1)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('close', () => {\n        t.strictEqual(signal.listenerCount('abort'), 0)\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: reqHeaders\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('passes socketPath to custom connect function', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const connectError = new Error('custom connect error')\n  const socketPath = '/var/run/test.sock'\n  const client = new Client('http://localhost', {\n    socketPath,\n    connect (opts, cb) {\n      t.strictEqual(opts.socketPath, socketPath)\n      cb(connectError, null)\n    }\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err, connectError)\n  })\n\n  await t.completed\n})\n\ntest('basic get with custom request.reset=true', async (t) => {\n  t = tspl(t, { plan: 26 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    t.strictEqual(req.headers.connection, 'close')\n    t.strictEqual(undefined, req.headers.foo)\n    t.strictEqual('bar', req.headers.bar)\n    t.strictEqual(undefined, req.headers['content-length'])\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {})\n    after(() => client.close())\n\n    t.strictEqual(client[kUrl].origin, `http://localhost:${server.address().port}`)\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/',\n      method: 'GET',\n      reset: true,\n      headers: reqHeaders\n    }, (err, data) => {\n      t.ifError(err)\n      const { statusCode, headers, body } = data\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(signal.listenerCount('abort'), 1)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('close', () => {\n        t.strictEqual(signal.listenerCount('abort'), 0)\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n\n    client.request({\n      path: '/',\n      reset: true,\n      method: 'GET',\n      headers: reqHeaders\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic get with query params', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const searchParamsObject = buildParams(req.url)\n    t.deepStrictEqual(searchParamsObject, {\n      bool: 'true',\n      foo: '1',\n      bar: 'bar',\n      '%60~%3A%24%2C%2B%5B%5D%40%5E*()-': '%60~%3A%24%2C%2B%5B%5D%40%5E*()-',\n      multi: ['1', '2'],\n      nullVal: '',\n      undefinedVal: ''\n    })\n\n    res.statusCode = 200\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const query = {\n    bool: true,\n    foo: 1,\n    bar: 'bar',\n    nullVal: null,\n    undefinedVal: undefined,\n    '`~:$,+[]@^*()-': '`~:$,+[]@^*()-',\n    multi: [1, 2]\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/',\n      method: 'GET',\n      query\n    }, (err, data) => {\n      t.ifError(err)\n      const { statusCode } = data\n      t.strictEqual(statusCode, 200)\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n  })\n\n  await t.completed\n})\n\ntest('basic get with query params fails if url includes hashmark', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail()\n  })\n  after(() => server.close())\n\n  const query = {\n    foo: 1,\n    bar: 'bar',\n    multi: [1, 2]\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/#',\n      method: 'GET',\n      query\n    }, (err, data) => {\n      t.strictEqual(err.message, 'Query params cannot be passed when url already contains \"?\" or \"#\".')\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic get with empty query params', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const searchParamsObject = buildParams(req.url)\n    t.deepStrictEqual(searchParamsObject, {})\n\n    res.statusCode = 200\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const query = {}\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/',\n      method: 'GET',\n      query\n    }, (err, data) => {\n      t.ifError(err)\n      const { statusCode } = data\n      t.strictEqual(statusCode, 200)\n    })\n    t.strictEqual(signal.listenerCount('abort'), 1)\n  })\n\n  await t.completed\n})\n\ntest('basic get with query params partially in path', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail()\n  })\n  after(() => server.close())\n\n  const query = {\n    foo: 1\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/?bar=2',\n      method: 'GET',\n      query\n    }, (err, data) => {\n      t.strictEqual(err.message, 'Query params cannot be passed when url already contains \"?\" or \"#\".')\n    })\n  })\n\n  await t.completed\n})\n\ntest('using throwOnError should throw (request)', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 400\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    const signal = new EE()\n    client.request({\n      signal,\n      path: '/',\n      method: 'GET',\n      throwOnError: true\n    }, (err) => {\n      t.strictEqual(err.message, 'invalid throwOnError')\n      t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n    })\n  })\n\n  await t.completed\n})\n\ntest('using throwOnError should throw (stream)', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 400\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeout: 300e3\n    })\n    after(() => client.close())\n\n    client.stream({\n      path: '/',\n      method: 'GET',\n      throwOnError: true,\n      opaque: new PassThrough()\n    }, ({ opaque: pt }) => {\n      pt.on('data', () => {\n        t.fail()\n      })\n      return pt\n    }, err => {\n      t.strictEqual(err.message, 'invalid throwOnError')\n      t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic head', async (t) => {\n  t = tspl(t, { plan: 14 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/123', req.url)\n    t.strictEqual('HEAD', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/123', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n\n    client.request({ path: '/123', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic head (IPv6)', { skip: !hasIPv6 }, async (t) => {\n  t = tspl(t, { plan: 10 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/123', req.url)\n    t.strictEqual('HEAD', req.method)\n    t.strictEqual(`[::1]:${server.address().port}`, req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, '::', () => {\n    const client = new Client(`http://[::1]:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/123', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n\n    client.request({ path: '/123', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('get with host header', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual('example.com', req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello from ' + req.headers.host)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET', headers: { host: 'example.com' } }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello from example.com', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('get with host header (IPv6)', { skip: !hasIPv6 }, async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual('[::1]', req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello from ' + req.headers.host)\n  })\n  after(() => server.close())\n\n  server.listen(0, '::', () => {\n    const client = new Client(`http://[::1]:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET', headers: { host: '[::1]' } }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello from [::1]', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('head with host header', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('HEAD', req.method)\n    t.strictEqual('example.com', req.headers.host)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello from ' + req.headers.host)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'HEAD', headers: { host: 'example.com' } }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      body\n        .resume()\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n\nfunction postServer (t, expected) {\n  return function (req, res) {\n    t.strictEqual(req.url, '/')\n    t.strictEqual(req.method, 'POST')\n    t.notStrictEqual(req.headers['content-length'], null)\n\n    req.setEncoding('utf8')\n    let data = ''\n\n    req.on('data', function (d) { data += d })\n\n    req.on('end', () => {\n      t.strictEqual(data, expected)\n      res.end('hello')\n    })\n  }\n}\n\ntest('basic POST with string', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'POST', body: expected }, (err, data) => {\n      t.ifError(err)\n      t.strictEqual(data.statusCode, 200)\n      const bufs = []\n      data.body\n        .on('data', (buf) => {\n          bufs.push(buf)\n        })\n        .on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with empty string', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, ''))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'POST', body: '' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with string and content-length', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-length': Buffer.byteLength(expected)\n      },\n      body: expected\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with Buffer', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename)\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected.toString()))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'POST', body: expected }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with stream', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-length': Buffer.byteLength(expected)\n      },\n      headersTimeout: 0,\n      body: createReadStream(__filename)\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with paused stream', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const stream = createReadStream(__filename)\n    stream.pause()\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-length': Buffer.byteLength(expected)\n      },\n      headersTimeout: 0,\n      body: stream\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with custom stream', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.resume().on('end', () => {\n      res.end('hello')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const body = new EE()\n    body.pipe = () => {}\n    client.request({\n      path: '/',\n      method: 'POST',\n      headersTimeout: 0,\n      body\n    }, (err, data) => {\n      t.ifError(err)\n      t.strictEqual(data.statusCode, 200)\n      const bufs = []\n      data.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      data.body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n    t.deepStrictEqual(client[kBusy], true)\n\n    body.on('close', () => {\n      body.emit('end')\n    })\n\n    client.on('connect', () => {\n      setImmediate(() => {\n        body.emit('data', '')\n        while (!client[kSocket]._writableState.needDrain) {\n          body.emit('data', Buffer.alloc(4096))\n        }\n        client[kSocket].on('drain', () => {\n          body.emit('data', Buffer.alloc(4096))\n          body.emit('close')\n        })\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with iterator', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const expected = 'hello'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.resume().on('end', () => {\n      res.end(expected)\n    })\n  })\n  after(() => server.close())\n\n  const iterable = {\n    [Symbol.iterator]: function * () {\n      for (let i = 0; i < expected.length - 1; i++) {\n        yield expected[i]\n      }\n      return expected[expected.length - 1]\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      requestTimeout: 0,\n      body: iterable\n    }, (err, { statusCode, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with iterator with invalid data', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, () => {})\n  after(() => server.close())\n\n  const iterable = {\n    [Symbol.iterator]: function * () {\n      yield 0\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      requestTimeout: 0,\n      body: iterable\n    }, err => {\n      t.ok(err instanceof TypeError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with async iterator', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-length': Buffer.byteLength(expected)\n      },\n      headersTimeout: 0,\n      body: wrapWithAsyncIterable(createReadStream(__filename))\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with transfer encoding: chunked', async (t) => {\n  t = tspl(t, { plan: 8 })\n\n  let body\n  const server = createServer({ joinDuplicateHeaders: true }, function (req, res) {\n    t.strictEqual(req.url, '/')\n    t.strictEqual(req.method, 'POST')\n    t.strictEqual(req.headers['content-length'], undefined)\n    t.strictEqual(req.headers['transfer-encoding'], 'chunked')\n\n    body.push(null)\n\n    req.setEncoding('utf8')\n    let data = ''\n\n    req.on('data', function (d) { data += d })\n\n    req.on('end', () => {\n      t.strictEqual(data, 'asd')\n      res.end('hello')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    body = new Readable({\n      read () { }\n    })\n    body.push('asd')\n    client.request({\n      path: '/',\n      method: 'POST',\n      // no content-length header\n      body\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic POST with empty stream', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, function (req, res) {\n    t.deepStrictEqual(req.headers['content-length'], '0')\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const body = new Readable({\n      autoDestroy: false,\n      read () {\n      },\n      destroy (err, callback) {\n        callback(!this._readableState.endEmitted ? new Error('asd') : err)\n      }\n    }).on('end', () => {\n      process.nextTick(() => {\n        t.strictEqual(body.destroyed, true)\n      })\n    })\n    body.push(null)\n    client.request({\n      path: '/',\n      method: 'POST',\n      body\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      body\n        .on('data', () => {\n          t.fail()\n        })\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('10 times GET', async (t) => {\n  const num = 10\n  t = tspl(t, { plan: 3 * num })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    for (let i = 0; i < num; i++) {\n      makeRequest(i)\n    }\n\n    function makeRequest (i) {\n      client.request({ path: '/' + i, method: 'GET' }, (err, { statusCode, headers, body }) => {\n        t.ifError(err)\n        t.strictEqual(statusCode, 200)\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          t.strictEqual('/' + i, Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('10 times HEAD', async (t) => {\n  const num = 10\n  t = tspl(t, { plan: num * 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    for (let i = 0; i < num; i++) {\n      makeRequest(i)\n    }\n\n    function makeRequest (i) {\n      client.request({ path: '/' + i, method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n        t.ifError(err)\n        t.strictEqual(statusCode, 200)\n        body\n          .resume()\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('Set-Cookie', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.setHeader('Set-Cookie', ['a cookie', 'another cookie', 'more cookies'])\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.deepStrictEqual(headers['set-cookie'], ['a cookie', 'another cookie', 'more cookies'])\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('ignore request header mutations', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers.test, 'test')\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const headers = { test: 'test' }\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.resume()\n    })\n    headers.test = 'asd'\n  })\n\n  await t.completed\n})\n\ntest('url-like url', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client({\n      hostname: 'localhost',\n      port: server.address().port,\n      protocol: 'http:'\n    })\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('an absolute url as path', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const path = 'http://example.com'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.url, path)\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client({\n      hostname: 'localhost',\n      port: server.address().port,\n      protocol: 'http:'\n    })\n    after(() => client.close())\n\n    client.request({ path, method: 'GET' }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('multiple destroy callback', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client({\n      hostname: 'localhost',\n      port: server.address().port,\n      protocol: 'http:'\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .resume()\n        .on('error', (err) => {\n          t.ok(err instanceof Error)\n        })\n      client.destroy(new Error(), (err) => {\n        t.ifError(err)\n      })\n      client.destroy(new Error(), (err) => {\n        t.ifError(err)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('only one streaming req at a time', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 4\n    })\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body.resume()\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        idempotent: true,\n        body: new Readable({\n          read () {\n            setImmediate(() => {\n              t.strictEqual(client[kBusy], true)\n              this.push(null)\n            })\n          }\n        }).on('resume', () => {\n          t.strictEqual(client[kSize], 1)\n        })\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n      t.strictEqual(client[kBusy], true)\n    })\n  })\n\n  await t.completed\n})\n\ntest('only one async iterating req at a time', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 4\n    })\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body.resume()\n      })\n      const body = wrapWithAsyncIterable(new Readable({\n        read () {\n          setImmediate(() => {\n            t.strictEqual(client[kBusy], true)\n            this.push(null)\n          })\n        }\n      }))\n      client.request({\n        path: '/',\n        method: 'PUT',\n        idempotent: true,\n        body\n      }, (err, data) => {\n        t.ifError(err)\n        data.body\n          .resume()\n          .on('end', () => {\n            t.ok(true, 'pass')\n          })\n      })\n      t.strictEqual(client[kBusy], true)\n    })\n  })\n\n  await t.completed\n})\n\ntest('300 requests succeed', async (t) => {\n  t = tspl(t, { plan: 300 * 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    for (let n = 0; n < 300; ++n) {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body.on('data', (chunk) => {\n          t.strictEqual(chunk.toString(), 'asd')\n        }).on('end', () => {\n          t.ok(true, 'pass')\n        })\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('request args validation', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:5000')\n\n  client.request(null, (err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  try {\n    client.request(null, 'asd')\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n\n  await t.completed\n})\n\ntest('request args validation promise', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const client = new Client('http://localhost:5000')\n\n  client.request(null).catch((err) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  await t.completed\n})\n\ntest('increase pipelining', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.resume()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      blocking: false\n    }, () => {\n      if (!client.destroyed) {\n        t.fail()\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      blocking: false\n    }, () => {\n      if (!client.destroyed) {\n        t.fail()\n      }\n    })\n\n    t.strictEqual(client[kRunning], 0)\n    client.on('connect', () => {\n      t.strictEqual(client[kRunning], 0)\n      process.nextTick(() => {\n        t.strictEqual(client[kRunning], 1)\n        client.pipelining = 3\n        t.strictEqual(client[kRunning], 2)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('destroy in push', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  let _res\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('asd')\n    _res = res\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, { body }) => {\n      t.ifError(err)\n      body.once('data', () => {\n        _res.write('asd')\n        body.on('data', (buf) => {\n          body.destroy()\n          _res.end()\n        }).on('error', (err) => {\n          t.ok(err)\n        })\n      })\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, { body }) => {\n      t.ifError(err)\n      let buf = ''\n      body.on('data', (chunk) => {\n        buf = chunk.toString()\n        _res.end()\n      }).on('end', () => {\n        t.strictEqual('asd', buf)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('non recoverable socket error fails pending request', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      t.strictEqual(err.message, 'kaboom')\n    })\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      t.strictEqual(err.message, 'kaboom')\n    })\n    client.on('connect', () => {\n      client[kSocket].destroy(new Error('kaboom'))\n    })\n  })\n\n  await t.completed\n})\n\ntest('POST empty with error', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const body = new Readable({\n      read () {\n      }\n    })\n    body.push(null)\n    client.on('connect', () => {\n      process.nextTick(() => {\n        body.emit('error', new Error('asd'))\n      })\n    })\n\n    client.request({ path: '/', method: 'POST', body }, (err, data) => {\n      t.strictEqual(err.message, 'asd')\n    })\n  })\n\n  await t.completed\n})\n\ntest('busy', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client[kConnect](() => {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err) => {\n        t.ifError(err)\n      })\n      t.strictEqual(client[kBusy], true)\n    })\n  })\n\n  await t.completed\n})\n\ntest('connected', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    // needed so that disconnect is emitted\n    res.setHeader('connection', 'close')\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const url = new URL(`http://localhost:${server.address().port}`)\n    const client = new Client(url, {\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client.on('connect', (origin, [self]) => {\n      t.strictEqual(origin, url)\n      t.strictEqual(client, self)\n    })\n    client.on('disconnect', (origin, [self]) => {\n      t.strictEqual(origin, url)\n      t.strictEqual(client, self)\n    })\n\n    t.strictEqual(client[kConnected], false)\n    client[kConnect](() => {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err) => {\n        t.ifError(err)\n      })\n      t.strictEqual(client[kConnected], true)\n    })\n  })\n\n  await t.completed\n})\n\ntest('emit disconnect after destroy', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const url = new URL(`http://localhost:${server.address().port}`)\n    const client = new Client(url)\n\n    t.strictEqual(client[kConnected], false)\n    client[kConnect](() => {\n      t.strictEqual(client[kConnected], true)\n      let disconnected = false\n      client.on('disconnect', () => {\n        disconnected = true\n        t.ok(true, 'pass')\n      })\n      client.destroy(() => {\n        t.strictEqual(disconnected, true)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('end response before request', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const readable = new Readable({\n      read () {\n        this.push('asd')\n      }\n    })\n    const { body } = await client.request({\n      method: 'GET',\n      path: '/',\n      body: readable\n    })\n    body\n      .on('error', () => {\n        t.fail()\n      })\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n      .resume()\n    client.on('disconnect', (url, targets, err) => {\n      t.strictEqual(err.code, 'UND_ERR_INFO')\n    })\n  })\n\n  await t.completed\n})\n\ntest('parser pause with no body timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    let counter = 0\n    const t = setInterval(() => {\n      counter++\n      const payload = Buffer.alloc(counter * 4096).fill(0)\n      if (counter === 3) {\n        clearInterval(t)\n        res.end(payload)\n      } else {\n        res.write(payload)\n      }\n    }, 20)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('TypedArray and DataView body', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers['content-length'], '8')\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n\n    const body = Uint8Array.from(Buffer.alloc(8))\n    client.request({ path: '/', method: 'POST', body }, (err, { statusCode, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('async iterator empty chunk continues', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const serverChunks = ['hello', 'world']\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    let str = ''\n    let i = 0\n    req.on('data', (chunk) => {\n      const content = chunk.toString()\n      t.strictEqual(serverChunks[i++], content)\n      str += content\n    }).on('end', () => {\n      t.strictEqual(str, serverChunks.join(''))\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n\n    const body = (async function * () {\n      yield serverChunks[0]\n      yield ''\n      yield serverChunks[1]\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err, { statusCode, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('async iterator error from server destroys early', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', (chunk) => {\n      res.destroy()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n    let gotDestroyed\n    const body = (async function * () {\n      try {\n        const promise = new Promise(resolve => {\n          gotDestroyed = resolve\n        })\n        yield 'hello'\n        await promise\n        yield 'inner-value'\n        t.fail('should not get here, iterator should be destroyed')\n      } finally {\n        t.ok(true, 'pass')\n      }\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err, { statusCode, body }) => {\n      t.ok(err)\n      t.strictEqual(statusCode, undefined)\n      gotDestroyed()\n    })\n  })\n\n  await t.completed\n})\n\ntest('regular iterator error from server closes early', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', () => {\n      process.nextTick(() => {\n        res.destroy()\n      })\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n    let gotDestroyed = false\n    const body = (function * () {\n      try {\n        yield 'start'\n        while (!gotDestroyed) {\n          yield 'zzz'\n          // for eslint\n          gotDestroyed = gotDestroyed || false\n        }\n        yield 'zzz'\n        t.fail('should not get here, iterator should be destroyed')\n        yield 'zzz'\n      } finally {\n        t.ok(true, 'pass')\n      }\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err, { statusCode, body }) => {\n      t.ok(err)\n      t.strictEqual(statusCode, undefined)\n      gotDestroyed = true\n    })\n  })\n  await t.completed\n})\n\ntest('async iterator early return closes early', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', () => {\n      res.writeHead(200)\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n    let gotDestroyed\n    const body = (async function * () {\n      try {\n        const promise = new Promise(resolve => {\n          gotDestroyed = resolve\n        })\n        yield 'hello'\n        await promise\n        yield 'inner-value'\n        t.fail('should not get here, iterator should be destroyed')\n      } finally {\n        t.ok(true, 'pass')\n      }\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err, { statusCode, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      gotDestroyed()\n    })\n  })\n  await t.completed\n})\n\ntest('async iterator yield unsupported TypedArray', {\n  skip: !!require('node:stream')._isArrayBufferView\n}, async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('end', () => {\n      res.writeHead(200)\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n    const body = (async function * () {\n      try {\n        yield new Int32Array([1])\n        t.fail('should not get here, iterator should be destroyed')\n      } finally {\n        t.ok(true, 'pass')\n      }\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err) => {\n      t.ok(err)\n      t.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE')\n    })\n  })\n\n  await t.completed\n})\n\ntest('async iterator yield object error', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('end', () => {\n      res.writeHead(200)\n      res.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0\n    })\n    after(() => client.close())\n    const body = (async function * () {\n      try {\n        yield {}\n        t.fail('should not get here, iterator should be destroyed')\n      } finally {\n        t.ok(true, 'pass')\n      }\n    })()\n    client.request({ path: '/', method: 'POST', body }, (err) => {\n      t.ok(err)\n      t.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Successfully get a Response when neither a Transfer-Encoding or Content-Length header is present', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.on('data', (data) => {\n    })\n    req.on('end', () => {\n      res.removeHeader('transfer-encoding')\n      res.writeHead(200, {\n        // Header isn't actually necessary, but tells node to close after response\n        connection: 'close',\n        foo: 'bar'\n      })\n      res.flushHeaders()\n      res.end('a response body')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, { body, headers }) => {\n      t.ifError(err)\n      t.equal(headers['content-length'], undefined)\n      t.equal(headers['transfer-encoding'], undefined)\n      const bufs = []\n      body.on('error', () => {\n        t.fail('Closing the connection is valid')\n      })\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.equal('a response body', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\nfunction buildParams (path) {\n  const cleanPath = path.replace('/?', '').replace('/', '').split('&')\n  const builtParams = cleanPath.reduce((acc, entry) => {\n    const [key, value] = entry.split('=')\n    if (key.length === 0) {\n      return acc\n    }\n\n    if (acc[key]) {\n      if (Array.isArray(acc[key])) {\n        acc[key].push(value)\n      } else {\n        acc[key] = [acc[key], value]\n      }\n    } else {\n      acc[key] = value\n    }\n    return acc\n  }, {})\n\n  return builtParams\n}\n\ntest('\\\\r\\\\n in Headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const reqHeaders = {\n    bar: '\\r\\nbar'\n  }\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: reqHeaders\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid bar header')\n  })\n})\n\ntest('\\\\r in Headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const reqHeaders = {\n    bar: '\\rbar'\n  }\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: reqHeaders\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid bar header')\n  })\n})\n\ntest('\\\\n in Headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const reqHeaders = {\n    bar: '\\nbar'\n  }\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: reqHeaders\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid bar header')\n  })\n})\n\ntest('\\\\n in Headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const reqHeaders = {\n    '\\nbar': 'foo'\n  }\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: reqHeaders\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid header key')\n  })\n})\n\ntest('\\\\n in Path', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/\\n',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid request path')\n  })\n})\n\ntest('\\\\n in Method', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const client = new Client('http://localhost:4242', {\n    keepAliveTimeout: 300e3\n  })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET\\n'\n  }, (err) => {\n    t.strictEqual(err.message, 'invalid request method')\n  })\n})\n\ntest('stats', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      t.strictEqual(client.stats.connected, true)\n      t.strictEqual(client.stats.pending, 0)\n      t.strictEqual(client.stats.running, 1)\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/close-and-destroy.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst { kSocket, kSize } = require('../lib/core/symbols')\n\ntest('close waits for queued requests to finish', async (t) => {\n  t = tspl(t, { plan: 16 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    t.ok(true, 'request received')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, function (err, data) {\n      onRequest(err, data)\n\n      client.request({ path: '/', method: 'GET' }, onRequest)\n      client.request({ path: '/', method: 'GET' }, onRequest)\n      client.request({ path: '/', method: 'GET' }, onRequest)\n\n      // needed because the next element in the queue will be called\n      // after the current function completes\n      process.nextTick(function () {\n        client.close()\n      })\n    })\n  })\n\n  function onRequest (err, { statusCode, headers, body }) {\n    t.ifError(err)\n    t.strictEqual(statusCode, 200)\n    const bufs = []\n    body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    body.on('end', () => {\n      t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n    })\n  }\n\n  await t.completed\n})\n\ntest('destroy invoked all pending callbacks', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.write('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      t.ifError(err)\n      data.body.on('error', (err) => {\n        t.ok(err)\n      }).resume()\n      client.destroy()\n    })\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('destroy invoked all pending callbacks ticked', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.write('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.destroy())\n\n    let ticked = false\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.strictEqual(ticked, true)\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.strictEqual(ticked, true)\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n    client.destroy()\n    ticked = true\n  })\n\n  await t.completed\n})\n\ntest('close waits until socket is destroyed', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    makeRequest()\n\n    client.once('connect', () => {\n      let done = false\n      client[kSocket].on('close', () => {\n        done = true\n      })\n      client.close((err) => {\n        t.ifError(err)\n        t.strictEqual(client.closed, true)\n        t.strictEqual(done, true)\n      })\n    })\n\n    function makeRequest () {\n      client.request({ path: '/', method: 'GET' }, (err, data) => {\n        t.ifError(err)\n      })\n      return client[kSize] <= client.pipelining\n    }\n  })\n\n  await t.completed\n})\n\ntest('close should still reconnect', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    t.ok(makeRequest())\n    t.ok(!makeRequest())\n\n    client.close((err) => {\n      t.ifError(err)\n      t.strictEqual(client.closed, true)\n    })\n    client.once('connect', () => {\n      client[kSocket].destroy()\n    })\n\n    function makeRequest () {\n      client.request({ path: '/', method: 'GET' }, (err, data) => {\n        t.ifError(err)\n        data.body.resume()\n      })\n      return client[kSize] <= client.pipelining\n    }\n  })\n\n  await t.completed\n})\n\ntest('close should call callback once finished', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setImmediate(function () {\n      res.end(req.url)\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    t.ok(makeRequest())\n    t.ok(!makeRequest())\n\n    client.close((err) => {\n      t.ifError(err)\n      t.strictEqual(client.closed, true)\n    })\n\n    function makeRequest () {\n      client.request({ path: '/', method: 'GET' }, (err, data) => {\n        t.ifError(err)\n        data.body.resume()\n      })\n      return client[kSize] <= client.pipelining\n    }\n  })\n\n  await t.completed\n})\n\ntest('closed and destroyed errors', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const client = new Client('http://localhost:4000')\n  after(() => client.destroy())\n\n  client.request({ path: '/', method: 'GET' }, (err) => {\n    t.ok(err)\n  })\n  client.close((err) => {\n    t.ifError(err)\n  })\n  client.request({ path: '/', method: 'GET' }, (err) => {\n    t.ok(err instanceof errors.ClientClosedError)\n    client.destroy()\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('close after and destroy should error', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:4000')\n  after(() => client.destroy())\n\n  client.destroy()\n  client.close((err) => {\n    t.ok(err instanceof errors.ClientDestroyedError)\n  })\n  client.close().catch((err) => {\n    t.ok(err instanceof errors.ClientDestroyedError)\n  })\n\n  await t.completed\n})\n\ntest('close socket and reconnect after maxRequestsPerClient reached', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    let connections = 0\n    server.on('connection', () => {\n      connections++\n    })\n    const client = new Client(\n      `http://localhost:${server.address().port}`,\n      { maxRequestsPerClient: 2 }\n    )\n    after(() => client.destroy())\n\n    await makeRequest()\n    await makeRequest()\n    await makeRequest()\n    await makeRequest()\n    t.strictEqual(connections, 2)\n\n    function makeRequest () {\n      return client.request({ path: '/', method: 'GET' })\n    }\n  })\n\n  await t.completed\n})\n\ntest('close socket and reconnect after maxRequestsPerClient reached (async)', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    let connections = 0\n    server.on('connection', () => {\n      connections++\n    })\n    const client = new Client(\n      `http://localhost:${server.address().port}`,\n      { maxRequestsPerClient: 2 }\n    )\n    after(() => client.destroy())\n\n    await Promise.all([\n      makeRequest(),\n      makeRequest(),\n      makeRequest(),\n      makeRequest()\n    ])\n    t.strictEqual(connections, 2)\n\n    function makeRequest () {\n      return client.request({ path: '/', method: 'GET' })\n    }\n  })\n\n  await t.completed\n})\n\ntest('should not close socket when no maxRequestsPerClient is provided', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(req.url)\n  })\n\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    let connections = 0\n    server.on('connection', () => {\n      connections++\n    })\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    await makeRequest()\n    await makeRequest()\n    await makeRequest()\n    await makeRequest()\n    t.strictEqual(connections, 1)\n\n    function makeRequest () {\n      return client.request({ path: '/', method: 'GET' })\n    }\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/connect-abort.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { Client } = require('..')\nconst { PassThrough } = require('node:stream')\n\ntest('connect-abort', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:1234', {\n    connect: (_, cb) => {\n      client.destroy()\n      cb(null, new PassThrough({\n        destroy (err, cb) {\n          t.strictEqual(err.name, 'ClientDestroyedError')\n          cb(null)\n        }\n      }))\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.strictEqual(err.name, 'ClientDestroyedError')\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/connect-errconnect.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst net = require('node:net')\n\ntest('connect-connectionError', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Client('http://localhost:9000')\n  after(() => client.close())\n\n  client.once('connectionError', () => {\n    t.ok(true, 'pass')\n  })\n\n  const _err = new Error('kaboom')\n  net.connect = function (options) {\n    const socket = new net.Socket(options)\n    setImmediate(() => {\n      socket.destroy(_err)\n    })\n    return socket\n  }\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err, _err)\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/connect-pre-shared-session.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after, mock } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:https')\nconst pem = require('@metcoder95/https-pem')\nconst tls = require('node:tls')\n\ntest('custom session passed to client will be used in tls connect call', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const mockConnect = mock.method(tls, 'connect')\n\n  const server = createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const session = Buffer.from('test-session')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false,\n        session\n      }\n    })\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const { statusCode, headers, body } = await client.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    t.strictEqual(statusCode, 200)\n    t.strictEqual(headers['content-type'], 'text/plain')\n\n    const responseText = await body.text()\n    t.strictEqual('hello', responseText)\n\n    const connectSession = mockConnect.mock.calls[0].arguments[0].session\n    t.strictEqual(connectSession, session)\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/connect-timeout.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after, describe } = require('node:test')\nconst { Client, Pool, errors } = require('..')\nconst net = require('node:net')\nconst assert = require('node:assert')\n\nconst skip = !!process.env.CITGM\n\n// Using describe instead of test to avoid the timeout\ndescribe('prioritize socket errors over timeouts', { skip }, async () => {\n  const t = tspl({ ...assert, after: () => {} }, { plan: 2 })\n  const client = new Pool('http://foorbar.invalid:1234', { connectTimeout: 1 })\n\n  client.request({ method: 'GET', path: '/foobar' })\n    .then(() => t.fail())\n    .catch((err) => {\n      t.strictEqual(err.code, 'ENOTFOUND')\n      t.strictEqual(err.code !== 'UND_ERR_CONNECT_TIMEOUT', true)\n    })\n\n  // block for 1s which is enough for the dns lookup to complete and the\n  // Timeout to fire\n  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Number(1000))\n\n  await t.completed\n})\n\n// mock net.connect to avoid the dns lookup\nnet.connect = function (options) {\n  return new net.Socket(options)\n}\n\ntest('connect-timeout', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n\n  const client = new Client('http://localhost:9000', {\n    connectTimeout: 1e3\n  })\n  after(() => client.close())\n\n  const timeout = setTimeout(() => {\n    t.fail()\n  }, 2e3)\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.ok(err instanceof errors.ConnectTimeoutError)\n    t.strictEqual(err.code, 'UND_ERR_CONNECT_TIMEOUT')\n    t.strictEqual(err.message, 'Connect Timeout Error (attempted address: localhost:9000, timeout: 1000ms)')\n    clearTimeout(timeout)\n  })\n\n  await t.completed\n})\n\ntest('connect-timeout', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n\n  const client = new Pool('http://localhost:9000', {\n    connectTimeout: 1e3\n  })\n  after(() => client.close())\n\n  const timeout = setTimeout(() => {\n    t.fail()\n  }, 2e3)\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.ok(err instanceof errors.ConnectTimeoutError)\n    t.strictEqual(err.code, 'UND_ERR_CONNECT_TIMEOUT')\n    t.strictEqual(err.message, 'Connect Timeout Error (attempted address: localhost:9000, timeout: 1000ms)')\n    clearTimeout(timeout)\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/content-length.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { maybeWrapStream, consts } = require('./utils/async-iterators')\n\ntest('request invalid content-length', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      headers: {\n        'content-length': 10\n      },\n      body: 'asd'\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      headers: {\n        'content-length': 10\n      },\n      body: 'asdasdasdasdasdasda'\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      headers: {\n        'content-length': 10\n      },\n      body: Buffer.alloc(9)\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      headers: {\n        'content-length': 10\n      },\n      body: Buffer.alloc(11)\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'content-length': 4\n      },\n      body: ['asd']\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'content-length': 4\n      },\n      body: ['asasdasdasdd']\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'DELETE',\n      headers: {\n        'content-length': 4\n      },\n      body: ['asasdasdasdd']\n    }, (err, data) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n  })\n\n  await t.completed\n})\n\nfunction invalidContentLength (bodyType) {\n  test(`request streaming ${bodyType} invalid content-length`, async (t) => {\n    t = tspl(t, { plan: 4 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => server.close())\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      after(() => client.close())\n\n      client.once('disconnect', () => {\n        t.ok(true, 'pass')\n        client.once('disconnect', () => {\n          t.ok(true, 'pass')\n        })\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: maybeWrapStream(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('asdasdasdkajsdnasdkjasnd')\n              this.push(null)\n            })\n          }\n        }), bodyType)\n      }, (err, data) => {\n        t.ok(err instanceof errors.RequestContentLengthMismatchError)\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: maybeWrapStream(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('asd')\n              this.push(null)\n            })\n          }\n        }), bodyType)\n      }, (err, data) => {\n        t.ok(err instanceof errors.RequestContentLengthMismatchError)\n      })\n    })\n    await t.completed\n  })\n}\n\ninvalidContentLength(consts.STREAM)\ninvalidContentLength(consts.ASYNC_ITERATOR)\n\nfunction zeroContentLength (bodyType) {\n  test(`request ${bodyType} streaming data when content-length=0`, async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => server.close())\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      after(() => client.close())\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 0\n        },\n        body: maybeWrapStream(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('asdasdasdkajsdnasdkjasnd')\n              this.push(null)\n            })\n          }\n        }), bodyType)\n      }, (err, data) => {\n        t.ok(err instanceof errors.RequestContentLengthMismatchError)\n      })\n    })\n    await t.completed\n  })\n}\n\nzeroContentLength(consts.STREAM)\nzeroContentLength(consts.ASYNC_ITERATOR)\n\ntest('request streaming no body data when content-length=0', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      headers: {\n        'content-length': 0\n      }\n    }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .on('data', () => {\n          t.fail()\n        })\n        .on('end', () => {\n          t.ok(true, 'pass')\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('response invalid content length with close', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'content-length': 10\n    })\n    res.end('123')\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 0\n    })\n    after(() => client.close())\n\n    client.on('disconnect', (origin, client, err) => {\n      t.strictEqual(err.code, 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH')\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .on('end', () => {\n          t.fail()\n        })\n        .on('error', (err) => {\n          t.strictEqual(err.code, 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH')\n        })\n        .resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('request streaming with Readable.from(buf)', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'PUT',\n      body: Readable.from(Buffer.from('hello'))\n    }, (err, data) => {\n      const chunks = []\n      t.ifError(err)\n      data.body\n        .on('data', (chunk) => {\n          chunks.push(chunk)\n        })\n        .on('end', () => {\n          t.strictEqual(Buffer.concat(chunks).toString(), 'hello')\n          t.ok(true, 'pass')\n          t.end()\n        })\n    })\n  })\n\n  await t.completed\n})\n\ntest('request DELETE, content-length=0, with body', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.shouldKeepAlive = false\n    res.end()\n  })\n  server.on('request', (req, res) => {\n    t.strictEqual(req.headers['content-length'], undefined)\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'DELETE',\n      headers: {\n        'content-length': 0\n      },\n      body: new Readable({\n        read () {\n          this.push('asd')\n          this.push(null)\n        }\n      })\n    }, (err) => {\n      t.ok(err instanceof errors.RequestContentLengthMismatchError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'DELETE',\n      headers: {\n        'content-length': 0\n      }\n    }, (err, resp) => {\n      t.strictEqual(resp.headers['content-length'], '0')\n      t.ifError(err)\n    })\n\n    client.on('disconnect', () => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n\ntest('content-length shouldSendContentLength=false', async (t) => {\n  t = tspl(t, { plan: 15 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  server.on('request', (req, res) => {\n    switch (req.url) {\n      case '/put0':\n        t.strictEqual(req.headers['content-length'], '0')\n        break\n      case '/head':\n        t.strictEqual(req.headers['content-length'], undefined)\n        break\n      case '/get':\n        t.strictEqual(req.headers['content-length'], undefined)\n        break\n    }\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/put0',\n      method: 'PUT',\n      headers: {\n        'content-length': 0\n      }\n    }, (err, resp) => {\n      t.strictEqual(resp.headers['content-length'], '0')\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/head',\n      method: 'HEAD',\n      headers: {\n        'content-length': 10\n      }\n    }, (err, resp) => {\n      t.strictEqual(resp.headers['content-length'], undefined)\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/get',\n      method: 'GET',\n      headers: {\n        'content-length': 0\n      }\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'content-length': 4\n      },\n      body: new Readable({\n        read () {\n          this.push('asd')\n          this.push(null)\n        }\n      })\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'content-length': 4\n      },\n      body: new Readable({\n        read () {\n          this.push('asasdasdasdd')\n          this.push(null)\n        }\n      })\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'HEAD',\n      headers: {\n        'content-length': 4\n      },\n      body: new Readable({\n        read () {\n          this.push('asasdasdasdd')\n          this.push(null)\n        }\n      })\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.on('disconnect', () => {\n      t.ok(true, 'pass')\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/cookie/cookies.js",
    "content": "// MIT License\n//\n// Copyright 2018-2022 the Deno authors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst {\n  deleteCookie,\n  getCookies,\n  getSetCookies,\n  setCookie,\n  Headers\n} = require('../..')\n\n// https://raw.githubusercontent.com/denoland/deno_std/b4239898d6c6b4cdbfd659a4ea1838cf4e656336/http/cookie_test.ts\n\ntest('Cookie parser', () => {\n  let headers = new Headers()\n  assert.deepEqual(getCookies(headers), {})\n  headers = new Headers()\n  headers.set('Cookie', 'foo=bar')\n  assert.deepEqual(getCookies(headers), { foo: 'bar' })\n\n  headers = new Headers()\n  headers.set('Cookie', 'full=of  ; tasty=chocolate')\n  assert.deepEqual(getCookies(headers), { full: 'of  ', tasty: 'chocolate' })\n\n  headers = new Headers()\n  headers.set('Cookie', 'igot=99; problems=but...')\n  assert.deepEqual(getCookies(headers), { igot: '99', problems: 'but...' })\n\n  headers = new Headers()\n  headers.set('Cookie', 'PREF=al=en-GB&f1=123; wide=1; SID=123')\n  assert.deepEqual(getCookies(headers), {\n    PREF: 'al=en-GB&f1=123',\n    wide: '1',\n    SID: '123'\n  })\n})\n\ntest('Cookie Name Validation', () => {\n  const tokens = [\n    '\"id\"',\n    'id\\t',\n    'i\\td',\n    'i d',\n    'i;d',\n    '{id}',\n    '[id]',\n    '\"',\n    'id\\u0091'\n  ]\n  const headers = new Headers()\n  tokens.forEach((name) => {\n    assert.throws(\n      () => {\n        setCookie(headers, {\n          name,\n          value: 'Cat',\n          httpOnly: true,\n          secure: true,\n          maxAge: 3\n        })\n      },\n      new Error('Invalid cookie name')\n    )\n  })\n})\n\ntest('Cookie Value Validation', () => {\n  const tokens = [\n    '1f\\tWa',\n    '\\t',\n    '1f Wa',\n    '1f;Wa',\n    '\"1fWa',\n    '1f\\\\Wa',\n    '1f\"Wa',\n    '\"',\n    '1fWa\\u0005',\n    '1f\\u0091Wa'\n  ]\n\n  const headers = new Headers()\n  tokens.forEach((value) => {\n    assert.throws(\n      () => {\n        setCookie(\n          headers,\n          {\n            name: 'Space',\n            value,\n            httpOnly: true,\n            secure: true,\n            maxAge: 3\n          }\n        )\n      },\n      new Error('Invalid cookie value'),\n      \"RFC2616 cookie 'Space'\"\n    )\n  })\n\n  assert.throws(\n    () => {\n      setCookie(headers, {\n        name: 'location',\n        value: 'United Kingdom'\n      })\n    },\n    new Error('Invalid cookie value'),\n    \"RFC2616 cookie 'location' cannot contain character ' '\"\n  )\n})\n\ntest('Cookie Path Validation', () => {\n  const path = '/;domain=sub.domain.com'\n  const headers = new Headers()\n  assert.throws(\n    () => {\n      setCookie(headers, {\n        name: 'Space',\n        value: 'Cat',\n        httpOnly: true,\n        secure: true,\n        path,\n        maxAge: 3\n      })\n    },\n    new Error('Invalid cookie path'),\n    path + \": Invalid cookie path char ';'\"\n  )\n})\n\ntest('Cookie Domain Validation', () => {\n  const tokens = ['-domain.com', 'domain.org.', 'domain.org-']\n  const headers = new Headers()\n  tokens.forEach((domain) => {\n    assert.throws(\n      () => {\n        setCookie(headers, {\n          name: 'Space',\n          value: 'Cat',\n          httpOnly: true,\n          secure: true,\n          domain,\n          maxAge: 3\n        })\n      },\n      new Error('Invalid cookie domain'),\n      'Invalid first/last char in cookie domain: ' + domain\n    )\n  })\n})\n\ntest('Cookie Delete', () => {\n  let headers = new Headers()\n  deleteCookie(headers, 'deno')\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'deno=; Expires=Thu, 01 Jan 1970 00:00:00 GMT'\n  )\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    domain: 'deno.land',\n    path: '/'\n  })\n  deleteCookie(headers, 'Space', { domain: '', path: '' })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Domain=deno.land; Path=/, Space=; Expires=Thu, 01 Jan 1970 00:00:00 GMT'\n  )\n})\n\ntest('Cookie Set', () => {\n  let headers = new Headers()\n  setCookie(headers, { name: 'Space', value: 'Cat' })\n  assert.equal(headers.get('Set-Cookie'), 'Space=Cat')\n\n  headers = new Headers()\n  setCookie(headers, { name: 'Space', value: 'Cat', secure: true })\n  assert.equal(headers.get('Set-Cookie'), 'Space=Cat; Secure')\n\n  headers = new Headers()\n  setCookie(headers, { name: 'Space', value: 'Cat', httpOnly: true })\n  assert.equal(headers.get('Set-Cookie'), 'Space=Cat; HttpOnly')\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true\n  })\n  assert.equal(headers.get('Set-Cookie'), 'Space=Cat; Secure; HttpOnly')\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 0\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=0'\n  )\n\n  let error = false\n  headers = new Headers()\n  try {\n    setCookie(headers, {\n      name: 'Space',\n      value: 'Cat',\n      httpOnly: true,\n      secure: true,\n      maxAge: -1\n    })\n  } catch {\n    error = true\n  }\n  assert.ok(error)\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land'\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    sameSite: 'Strict'\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; ' +\n        'SameSite=Strict'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    sameSite: 'Lax'\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; SameSite=Lax'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/'\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/',\n    unparsed: ['unparsed=keyvalue', 'batman=Bruce']\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; ' +\n        'unparsed=keyvalue; batman=Bruce'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/',\n    expires: new Date(Date.UTC(1983, 0, 7, 15, 32))\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; ' +\n        'Expires=Fri, 07 Jan 1983 15:32:00 GMT'\n  )\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: 'Space',\n    value: 'Cat',\n    expires: Date.UTC(1983, 0, 7, 15, 32)\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'Space=Cat; Expires=Fri, 07 Jan 1983 15:32:00 GMT'\n  )\n\n  headers = new Headers()\n  setCookie(headers, { name: '__Secure-Kitty', value: 'Meow' })\n  assert.equal(headers.get('Set-Cookie'), '__Secure-Kitty=Meow; Secure')\n\n  headers = new Headers()\n  setCookie(headers, {\n    name: '__Host-Kitty',\n    value: 'Meow',\n    domain: 'deno.land'\n  })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    '__Host-Kitty=Meow; Secure; Path=/'\n  )\n\n  headers = new Headers()\n  setCookie(headers, { name: 'cookie-1', value: 'value-1', secure: true })\n  setCookie(headers, { name: 'cookie-2', value: 'value-2', maxAge: 3600 })\n  assert.equal(\n    headers.get('Set-Cookie'),\n    'cookie-1=value-1; Secure, cookie-2=value-2; Max-Age=3600'\n  )\n\n  headers = new Headers()\n  setCookie(headers, { name: '', value: '' })\n  assert.equal(headers.get('Set-Cookie'), null)\n})\n\ntest('Set-Cookie parser', () => {\n  let headers = new Headers({ 'set-cookie': 'Space=Cat' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat'\n  }])\n\n  headers = new Headers({ 'set-cookie': 'Space=Cat; Secure' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true\n  }])\n\n  headers = new Headers({ 'set-cookie': 'Space=Cat; HttpOnly' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    httpOnly: true\n  }])\n\n  headers = new Headers({ 'set-cookie': 'Space=Cat; Secure; HttpOnly' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true\n  }])\n\n  headers = new Headers({\n    'set-cookie': 'Space=Cat; Secure; HttpOnly; Max-Age=2'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2\n  }])\n\n  headers = new Headers({\n    'set-cookie': 'Space=Cat; Secure; HttpOnly; Max-Age=0'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 0\n  }])\n\n  headers = new Headers({\n    'set-cookie': 'Space=Cat; Secure; HttpOnly; Max-Age=-1'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true\n  }])\n\n  headers = new Headers({\n    'set-cookie': 'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land'\n  }])\n\n  headers = new Headers({\n    'set-cookie':\n        'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; SameSite=Strict'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    sameSite: 'Strict'\n  }])\n\n  headers = new Headers({\n    'set-cookie':\n        'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; SameSite=Lax'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    sameSite: 'Lax'\n  }])\n\n  headers = new Headers({\n    'set-cookie':\n        'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/'\n  }])\n\n  headers = new Headers({\n    'set-cookie':\n        'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; unparsed=keyvalue; batman=Bruce'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/',\n    unparsed: ['unparsed=keyvalue', 'batman=Bruce']\n  }])\n\n  headers = new Headers({\n    'set-cookie':\n        'Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; ' +\n        'Expires=Fri, 07 Jan 1983 15:32:00 GMT'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: 'Space',\n    value: 'Cat',\n    secure: true,\n    httpOnly: true,\n    maxAge: 2,\n    domain: 'deno.land',\n    path: '/',\n    expires: new Date(Date.UTC(1983, 0, 7, 15, 32))\n  }])\n\n  headers = new Headers({ 'set-cookie': '__Secure-Kitty=Meow; Secure' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Secure-Kitty',\n    value: 'Meow',\n    secure: true\n  }])\n\n  headers = new Headers({ 'set-cookie': '__Secure-Kitty=Meow' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Secure-Kitty',\n    value: 'Meow'\n  }])\n\n  headers = new Headers({\n    'set-cookie': '__Host-Kitty=Meow; Secure; Path=/'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Host-Kitty',\n    value: 'Meow',\n    secure: true,\n    path: '/'\n  }])\n\n  headers = new Headers({ 'set-cookie': '__Host-Kitty=Meow; Path=/' })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Host-Kitty',\n    value: 'Meow',\n    path: '/'\n  }])\n\n  headers = new Headers({\n    'set-cookie': '__Host-Kitty=Meow; Secure; Domain=deno.land; Path=/'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Host-Kitty',\n    value: 'Meow',\n    secure: true,\n    domain: 'deno.land',\n    path: '/'\n  }])\n\n  headers = new Headers({\n    'set-cookie': '__Host-Kitty=Meow; Secure; Path=/not-root'\n  })\n  assert.deepEqual(getSetCookies(headers), [{\n    name: '__Host-Kitty',\n    value: 'Meow',\n    secure: true,\n    path: '/not-root'\n  }])\n\n  headers = new Headers([\n    ['set-cookie', 'cookie-1=value-1; Secure'],\n    ['set-cookie', 'cookie-2=value-2; Max-Age=3600']\n  ])\n  assert.deepEqual(getSetCookies(headers), [\n    { name: 'cookie-1', value: 'value-1', secure: true },\n    { name: 'cookie-2', value: 'value-2', maxAge: 3600 }\n  ])\n\n  headers = new Headers()\n  assert.deepEqual(getSetCookies(headers), [])\n})\n\ntest('Cookie setCookie throws if headers is not of type Headers', () => {\n  class Headers {\n    [Symbol.toStringTag] = 'CustomHeaders'\n  }\n  const headers = new Headers()\n  assert.throws(\n    () => {\n      setCookie(headers, {\n        name: 'key',\n        value: 'Cat',\n        httpOnly: true,\n        secure: true,\n        maxAge: 3\n      })\n    },\n    new TypeError('Illegal invocation')\n  )\n})\n\ntest('Cookie setCookie does not throw if headers is an instance of undici owns Headers class', () => {\n  const headers = new Headers()\n  setCookie(headers, {\n    name: 'key',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 3\n  })\n})\n\ntest('Cookie setCookie does not throw if headers is an instance of the global Headers class', { skip: !globalThis.Headers }, () => {\n  const headers = new globalThis.Headers()\n  setCookie(headers, {\n    name: 'key',\n    value: 'Cat',\n    httpOnly: true,\n    secure: true,\n    maxAge: 3\n  })\n})\n\ntest('Cookie getCookies throws if headers is not of type Headers', () => {\n  class Headers {\n    [Symbol.toStringTag] = 'CustomHeaders'\n  }\n  const headers = new Headers()\n  assert.throws(\n    () => {\n      getCookies(headers)\n    },\n    new TypeError('Illegal invocation')\n  )\n})\n\ntest('Cookie getCookies does not throw if headers is an instance of undici owns Headers class', () => {\n  const headers = new Headers()\n  getCookies(headers)\n})\n\ntest('Cookie getCookie does not throw if headers is an instance of the global Headers class', { skip: !globalThis.Headers }, () => {\n  const headers = new globalThis.Headers()\n  getCookies(headers)\n})\n\ntest('Cookie getSetCookies throws if headers is not of type Headers', () => {\n  class Headers {\n    [Symbol.toStringTag] = 'CustomHeaders'\n  }\n  const headers = new Headers({ 'set-cookie': 'Space=Cat' })\n  assert.throws(\n    () => {\n      getSetCookies(headers)\n    },\n    new TypeError('Illegal invocation')\n  )\n})\n\ntest('Cookie getSetCookies does not throw if headers is an instance of undici owns Headers class', () => {\n  const headers = new Headers({ 'set-cookie': 'Space=Cat' })\n  getSetCookies(headers)\n})\n\ntest('Cookie setCookie does not throw if headers is an instance of the global Headers class', { skip: !globalThis.Headers }, () => {\n  const headers = new globalThis.Headers({ 'set-cookie': 'Space=Cat' })\n  getSetCookies(headers)\n})\n\ntest('Cookie deleteCookie throws if headers is not of type Headers', () => {\n  class Headers {\n    [Symbol.toStringTag] = 'CustomHeaders'\n  }\n  const headers = new Headers()\n  assert.throws(\n    () => {\n      deleteCookie(headers, 'deno')\n    },\n    new TypeError('Illegal invocation')\n  )\n})\n\ntest('Cookie deleteCookie does not throw if headers is an instance of undici owns Headers class', () => {\n  const headers = new Headers()\n  deleteCookie(headers, 'deno')\n})\n\ntest('Cookie getCookie does not throw if headers is an instance of the global Headers class', { skip: !globalThis.Headers }, () => {\n  const headers = new globalThis.Headers()\n  deleteCookie(headers, 'deno')\n})\n"
  },
  {
    "path": "test/cookie/global-headers.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst {\n  deleteCookie,\n  getCookies,\n  getSetCookies,\n  setCookie\n} = require('../..')\n\ndescribe('Using global Headers', async () => {\n  test('deleteCookies', { skip: !globalThis.Headers }, () => {\n    const headers = new globalThis.Headers()\n\n    assert.equal(headers.get('set-cookie'), null)\n    deleteCookie(headers, 'undici')\n    assert.equal(headers.get('set-cookie'), 'undici=; Expires=Thu, 01 Jan 1970 00:00:00 GMT')\n  })\n\n  test('getCookies', { skip: !globalThis.Headers }, () => {\n    const headers = new globalThis.Headers({\n      cookie: 'get=cookies; and=attributes'\n    })\n\n    assert.deepEqual(getCookies(headers), { get: 'cookies', and: 'attributes' })\n  })\n\n  test('getSetCookies', { skip: !globalThis.Headers }, () => {\n    const headers = new globalThis.Headers({\n      'set-cookie': 'undici=getSetCookies; Secure'\n    })\n\n    const supportsCookies = headers.getSetCookie()\n\n    if (!supportsCookies) {\n      assert.deepEqual(getSetCookies(headers), [])\n    } else {\n      assert.deepEqual(getSetCookies(headers), [\n        {\n          name: 'undici',\n          value: 'getSetCookies',\n          secure: true\n        }\n      ])\n    }\n  })\n\n  test('setCookie', { skip: !globalThis.Headers }, () => {\n    const headers = new globalThis.Headers()\n\n    setCookie(headers, { name: 'undici', value: 'setCookie' })\n    assert.equal(headers.get('Set-Cookie'), 'undici=setCookie')\n  })\n})\n\ndescribe('Headers check is not too lax', { skip: !globalThis.Headers }, () => {\n  class Headers { }\n  Object.defineProperty(globalThis.Headers.prototype, Symbol.toStringTag, {\n    value: 'Headers',\n    configurable: true\n  })\n\n  assert.throws(() => getCookies(new Headers()), { code: 'ERR_INVALID_THIS' })\n  assert.throws(() => getSetCookies(new Headers()), { code: 'ERR_INVALID_THIS' })\n  assert.throws(() => setCookie(new Headers(), { name: 'a', value: 'b' }), { code: 'ERR_INVALID_THIS' })\n  assert.throws(() => deleteCookie(new Headers(), 'name'), { code: 'ERR_INVALID_THIS' })\n})\n"
  },
  {
    "path": "test/cookie/is-ctl-excluding-htab.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { strictEqual } = require('node:assert')\n\nconst {\n  isCTLExcludingHtab\n} = require('../../lib/web/cookies/util')\n\ndescribe('isCTLExcludingHtab', () => {\n  test('should return false for 0x00 - 0x08 characters', () => {\n    strictEqual(isCTLExcludingHtab('\\x00'), true)\n    strictEqual(isCTLExcludingHtab('\\x01'), true)\n    strictEqual(isCTLExcludingHtab('\\x02'), true)\n    strictEqual(isCTLExcludingHtab('\\x03'), true)\n    strictEqual(isCTLExcludingHtab('\\x04'), true)\n    strictEqual(isCTLExcludingHtab('\\x05'), true)\n    strictEqual(isCTLExcludingHtab('\\x06'), true)\n    strictEqual(isCTLExcludingHtab('\\x07'), true)\n    strictEqual(isCTLExcludingHtab('\\x08'), true)\n  })\n\n  test('should return false for 0x09 HTAB character', () => {\n    strictEqual(isCTLExcludingHtab('\\x09'), false)\n  })\n\n  test('should return false for 0x0A - 0x1F characters', () => {\n    strictEqual(isCTLExcludingHtab('\\x0A'), true)\n    strictEqual(isCTLExcludingHtab('\\x0B'), true)\n    strictEqual(isCTLExcludingHtab('\\x0C'), true)\n    strictEqual(isCTLExcludingHtab('\\x0D'), true)\n    strictEqual(isCTLExcludingHtab('\\x0E'), true)\n    strictEqual(isCTLExcludingHtab('\\x0F'), true)\n    strictEqual(isCTLExcludingHtab('\\x10'), true)\n    strictEqual(isCTLExcludingHtab('\\x11'), true)\n    strictEqual(isCTLExcludingHtab('\\x12'), true)\n    strictEqual(isCTLExcludingHtab('\\x13'), true)\n    strictEqual(isCTLExcludingHtab('\\x14'), true)\n    strictEqual(isCTLExcludingHtab('\\x15'), true)\n    strictEqual(isCTLExcludingHtab('\\x16'), true)\n    strictEqual(isCTLExcludingHtab('\\x17'), true)\n    strictEqual(isCTLExcludingHtab('\\x18'), true)\n    strictEqual(isCTLExcludingHtab('\\x19'), true)\n    strictEqual(isCTLExcludingHtab('\\x1A'), true)\n    strictEqual(isCTLExcludingHtab('\\x1B'), true)\n    strictEqual(isCTLExcludingHtab('\\x1C'), true)\n    strictEqual(isCTLExcludingHtab('\\x1D'), true)\n    strictEqual(isCTLExcludingHtab('\\x1E'), true)\n    strictEqual(isCTLExcludingHtab('\\x1F'), true)\n  })\n\n  test('should return false for a 0x7F character', t => {\n    strictEqual(isCTLExcludingHtab('\\x7F'), true)\n  })\n\n  test('should return false for a 0x20 / space character', t => {\n    strictEqual(isCTLExcludingHtab(' '), false)\n  })\n\n  test('should return false for a printable character', t => {\n    strictEqual(isCTLExcludingHtab('A'), false)\n    strictEqual(isCTLExcludingHtab('Z'), false)\n    strictEqual(isCTLExcludingHtab('a'), false)\n    strictEqual(isCTLExcludingHtab('z'), false)\n    strictEqual(isCTLExcludingHtab('!'), false)\n  })\n\n  test('should return false for an empty string', () => {\n    strictEqual(isCTLExcludingHtab(''), false)\n  })\n\n  test('all printable characters (0x20 - 0x7E)', () => {\n    for (let i = 0x20; i < 0x7F; i++) {\n      strictEqual(isCTLExcludingHtab(String.fromCharCode(i)), false)\n    }\n  })\n\n  test('valid case', () => {\n    strictEqual(isCTLExcludingHtab('Space=Cat; Secure; HttpOnly; Max-Age=2'), false)\n  })\n\n  test('invalid case', () => {\n    strictEqual(isCTLExcludingHtab('Space=Cat; Secure; HttpOnly; Max-Age=2\\x7F'), true)\n  })\n})\n"
  },
  {
    "path": "test/cookie/npm-cookie.js",
    "content": "'use strict'\n\n// (The MIT License)\n//\n// Copyright (c) 2012-2014 Roman Shtylman <shtylman@gmail.com>\n// Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining\n// a copy of this software and associated documentation files (the\n// 'Software'), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so, subject to\n// the following conditions:\n//\n// The above copyright notice and this permission notice shall be\n// included in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert')\nconst { parseCookie } = require('../..')\n\ndescribe('parseCookie(str)', function () {\n  it('should parse cookie string to object', function () {\n    assert.deepStrictEqual(parseCookie('foo=bar'), { name: 'foo', value: 'bar' })\n    assert.deepStrictEqual(parseCookie('foo=123'), { name: 'foo', value: '123' })\n  })\n\n  it('should ignore OWS', function () {\n    assert.deepStrictEqual(parseCookie('FOO    = bar;   baz  =   raz'), {\n      name: 'FOO',\n      value: 'bar',\n      unparsed: ['baz=raz']\n    })\n  })\n\n  it('should parse cookie with empty value', function () {\n    assert.deepStrictEqual(parseCookie('foo=; bar='), { name: 'foo', value: '', unparsed: ['bar='] })\n  })\n\n  it('should parse cookie with minimum length', function () {\n    assert.deepStrictEqual(parseCookie('f='), { name: 'f', value: '' })\n    assert.deepStrictEqual(parseCookie('f=;b='), { name: 'f', value: '', unparsed: ['b='] })\n  })\n\n  it('should URL-decode values', function () {\n    assert.deepStrictEqual(parseCookie('foo=\"bar=123456789&name=Magic+Mouse\"'), {\n      name: 'foo',\n      value: '\"bar=123456789&name=Magic+Mouse\"'\n    })\n\n    assert.deepStrictEqual(parseCookie('email=%20%22%2c%3b%2f'), { name: 'email', value: ' \",;/' })\n  })\n\n  it('should trim whitespace around key and value', function () {\n    assert.deepStrictEqual(parseCookie('  foo  =  \"bar\"  '), { name: 'foo', value: '\"bar\"' })\n    assert.deepStrictEqual(parseCookie('  foo  =  bar  ;  fizz  =  buzz  '), {\n      name: 'foo',\n      value: 'bar',\n      unparsed: ['fizz=buzz']\n    })\n    assert.deepStrictEqual(parseCookie(' foo = \" a b c \" '), { name: 'foo', value: '\" a b c \"' })\n    assert.deepStrictEqual(parseCookie(' = bar '), { name: '', value: 'bar' })\n    assert.deepStrictEqual(parseCookie(' foo = '), { name: 'foo', value: '' })\n    assert.deepStrictEqual(parseCookie('   =   '), { name: '', value: '' })\n    assert.deepStrictEqual(parseCookie('\\tfoo\\t=\\tbar\\t'), { name: 'foo', value: 'bar' })\n  })\n\n  it('should return original value on escape error', function () {\n    assert.deepStrictEqual(parseCookie('foo=%1;bar=bar'), { name: 'foo', value: '%1', unparsed: ['bar=bar'] })\n  })\n\n  it('should ignore cookies without value', function () {\n    assert.deepStrictEqual(parseCookie('foo=bar;fizz  ;  buzz'), { name: 'foo', value: 'bar', unparsed: ['fizz=', 'buzz='] })\n    assert.deepStrictEqual(parseCookie('  fizz; foo=  bar'), { name: '', value: 'fizz', unparsed: ['foo=bar'] })\n  })\n\n  it('should ignore duplicate cookies', function () {\n    assert.deepStrictEqual(parseCookie('foo=%1;bar=bar;foo=boo'), {\n      name: 'foo',\n      value: '%1',\n      unparsed: ['bar=bar', 'foo=boo']\n    })\n    assert.deepStrictEqual(parseCookie('foo=false;bar=bar;foo=true'), {\n      name: 'foo',\n      value: 'false',\n      unparsed: ['bar=bar', 'foo=true']\n    })\n    assert.deepStrictEqual(parseCookie('foo=;bar=bar;foo=boo'), {\n      name: 'foo',\n      value: '',\n      unparsed: ['bar=bar', 'foo=boo']\n    })\n  })\n\n  it('should parse native properties', function () {\n    assert.deepStrictEqual(parseCookie('toString=foo;valueOf=bar'), {\n      name: 'toString',\n      unparsed: [\n        'valueOf=bar'\n      ],\n      value: 'foo'\n    })\n  })\n})\n"
  },
  {
    "path": "test/cookie/to-imf-date.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { strictEqual } = require('node:assert')\n\nconst {\n  toIMFDate\n} = require('../../lib/web/cookies/util')\n\ndescribe('toIMFDate', () => {\n  test('should return the same as Date.prototype.toGMTString()', () => {\n    for (let i = 1; i <= 1e6; i *= 2) {\n      const date = new Date(i)\n      strictEqual(toIMFDate(date), date.toGMTString())\n    }\n    for (let i = 0; i <= 1e6; i++) {\n      const date = new Date(Math.trunc(Math.random() * 8640000000000000))\n      strictEqual(toIMFDate(date), date.toGMTString())\n    }\n  })\n})\n"
  },
  {
    "path": "test/cookie/validate-cookie-name.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { throws, strictEqual } = require('node:assert')\n\nconst {\n  validateCookieName\n} = require('../../lib/web/cookies/util')\n\ndescribe('validateCookieName', () => {\n  test('should throw for CTLs', () => {\n    throws(() => validateCookieName('\\x00'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x01'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x02'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x03'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x04'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x05'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x06'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x07'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x08'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x09'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0A'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0B'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0C'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0D'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0E'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x0F'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x10'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x11'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x12'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x13'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x14'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x15'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x16'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x17'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x18'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x19'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1A'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1B'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1C'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1D'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1E'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x1F'), new Error('Invalid cookie name'))\n    throws(() => validateCookieName('\\x7F'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for \" \" character', () => {\n    throws(() => validateCookieName(' '), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for Horizontal Tab character', () => {\n    throws(() => validateCookieName('\\t'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for ; character', () => {\n    throws(() => validateCookieName(';'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for \" character', () => {\n    throws(() => validateCookieName('\"'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for , character', () => {\n    throws(() => validateCookieName(','), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for \\\\ character', () => {\n    throws(() => validateCookieName('\\\\'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for ( character', () => {\n    throws(() => validateCookieName('('), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for ) character', () => {\n    throws(() => validateCookieName(')'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for < character', () => {\n    throws(() => validateCookieName('<'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for > character', () => {\n    throws(() => validateCookieName('>'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for @ character', () => {\n    throws(() => validateCookieName('@'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for : character', () => {\n    throws(() => validateCookieName(':'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for / character', () => {\n    throws(() => validateCookieName('/'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for [ character', () => {\n    throws(() => validateCookieName('['), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for ] character', () => {\n    throws(() => validateCookieName(']'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for ? character', () => {\n    throws(() => validateCookieName('?'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for = character', () => {\n    throws(() => validateCookieName('='), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for { character', () => {\n    throws(() => validateCookieName('{'), new Error('Invalid cookie name'))\n  })\n\n  test('should throw for } character', () => {\n    throws(() => validateCookieName('}'), new Error('Invalid cookie name'))\n  })\n\n  test('should pass for a printable character', t => {\n    strictEqual(validateCookieName('A'), undefined)\n    strictEqual(validateCookieName('Z'), undefined)\n    strictEqual(validateCookieName('a'), undefined)\n    strictEqual(validateCookieName('z'), undefined)\n    strictEqual(validateCookieName('!'), undefined)\n  })\n})\n"
  },
  {
    "path": "test/cookie/validate-cookie-path.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { throws, strictEqual } = require('node:assert')\n\nconst {\n  validateCookiePath\n} = require('../../lib/web/cookies/util')\n\ndescribe('validateCookiePath', () => {\n  test('should throw for CTLs', () => {\n    throws(() => validateCookiePath('\\x00'))\n    throws(() => validateCookiePath('\\x01'))\n    throws(() => validateCookiePath('\\x02'))\n    throws(() => validateCookiePath('\\x03'))\n    throws(() => validateCookiePath('\\x04'))\n    throws(() => validateCookiePath('\\x05'))\n    throws(() => validateCookiePath('\\x06'))\n    throws(() => validateCookiePath('\\x07'))\n    throws(() => validateCookiePath('\\x08'))\n    throws(() => validateCookiePath('\\x09'))\n    throws(() => validateCookiePath('\\x0A'))\n    throws(() => validateCookiePath('\\x0B'))\n    throws(() => validateCookiePath('\\x0C'))\n    throws(() => validateCookiePath('\\x0D'))\n    throws(() => validateCookiePath('\\x0E'))\n    throws(() => validateCookiePath('\\x0F'))\n    throws(() => validateCookiePath('\\x10'))\n    throws(() => validateCookiePath('\\x11'))\n    throws(() => validateCookiePath('\\x12'))\n    throws(() => validateCookiePath('\\x13'))\n    throws(() => validateCookiePath('\\x14'))\n    throws(() => validateCookiePath('\\x15'))\n    throws(() => validateCookiePath('\\x16'))\n    throws(() => validateCookiePath('\\x17'))\n    throws(() => validateCookiePath('\\x18'))\n    throws(() => validateCookiePath('\\x19'))\n    throws(() => validateCookiePath('\\x1A'))\n    throws(() => validateCookiePath('\\x1B'))\n    throws(() => validateCookiePath('\\x1C'))\n    throws(() => validateCookiePath('\\x1D'))\n    throws(() => validateCookiePath('\\x1E'))\n    throws(() => validateCookiePath('\\x1F'))\n    throws(() => validateCookiePath('\\x7F'))\n  })\n\n  test('should throw for ; character', () => {\n    throws(() => validateCookiePath(';'))\n  })\n\n  test('should pass for a printable character', t => {\n    strictEqual(validateCookiePath('A'), undefined)\n    strictEqual(validateCookiePath('Z'), undefined)\n    strictEqual(validateCookiePath('a'), undefined)\n    strictEqual(validateCookiePath('z'), undefined)\n    strictEqual(validateCookiePath('!'), undefined)\n    strictEqual(validateCookiePath(' '), undefined)\n  })\n})\n"
  },
  {
    "path": "test/cookie/validate-cookie-value.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { throws, strictEqual } = require('node:assert')\n\nconst {\n  validateCookieValue\n} = require('../../lib/web/cookies/util')\n\ndescribe('validateCookieValue', () => {\n  test('should throw for CTLs', () => {\n    throws(() => validateCookieValue('\\x00'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x01'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x02'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x03'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x04'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x05'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x06'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x07'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x08'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x09'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0A'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0B'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0C'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0D'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0E'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x0F'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x10'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x11'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x12'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x13'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x14'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x15'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x16'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x17'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x18'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x19'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1A'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1B'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1C'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1D'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1E'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x1F'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\\x7F'), new Error('Invalid cookie value'))\n  })\n\n  test('should throw for ; character', () => {\n    throws(() => validateCookieValue(';'), new Error('Invalid cookie value'))\n  })\n\n  test('should throw for \" character', () => {\n    throws(() => validateCookieValue('\"'), new Error('Invalid cookie value'))\n  })\n\n  test('should throw for , character', () => {\n    throws(() => validateCookieValue(','), new Error('Invalid cookie value'))\n  })\n\n  test('should throw for \\\\ character', () => {\n    throws(() => validateCookieValue('\\\\'), new Error('Invalid cookie value'))\n  })\n\n  test('should pass for a printable character', t => {\n    strictEqual(validateCookieValue('A'), undefined)\n    strictEqual(validateCookieValue('Z'), undefined)\n    strictEqual(validateCookieValue('a'), undefined)\n    strictEqual(validateCookieValue('z'), undefined)\n    strictEqual(validateCookieValue('!'), undefined)\n    strictEqual(validateCookieValue('='), undefined)\n  })\n\n  test('should handle strings wrapped in DQUOTE', t => {\n    strictEqual(validateCookieValue('\"\"'), undefined)\n    strictEqual(validateCookieValue('\"helloworld\"'), undefined)\n    throws(() => validateCookieValue('\"'), new Error('Invalid cookie value'))\n    throws(() => validateCookieValue('\"\"\"'), new Error('Invalid cookie value'))\n  })\n})\n"
  },
  {
    "path": "test/decorator-handler.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test } = require('node:test')\nconst DecoratorHandler = require('../lib/handler/decorator-handler')\n\ndescribe('DecoratorHandler', () => {\n  test('should throw if provided handler is not an object', t => {\n    t = tspl(t, { plan: 4 })\n    t.throws(\n      () => new DecoratorHandler(null),\n      new TypeError('handler must be an object')\n    )\n    t.throws(\n      () => new DecoratorHandler('string'),\n      new TypeError('handler must be an object')\n    )\n\n    t.throws(\n      () => new DecoratorHandler(null),\n      new TypeError('handler must be an object')\n    )\n    t.throws(\n      () => new DecoratorHandler('string'),\n      new TypeError('handler must be an object')\n    )\n  })\n\n  describe('wrap', () => {\n    const Handler = class {\n      #handler = null\n      constructor (handler) {\n        this.#handler = handler\n      }\n\n      onConnect (abort, context) {\n        return this.#handler?.onConnect?.(abort, context)\n      }\n\n      onHeaders (statusCode, rawHeaders, resume, statusMessage) {\n        return this.#handler?.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)\n      }\n\n      onUpgrade (statusCode, rawHeaders, socket) {\n        return this.#handler?.onUpgrade?.(statusCode, rawHeaders, socket)\n      }\n\n      onData (data) {\n        return this.#handler?.onData?.(data)\n      }\n\n      onComplete (trailers) {\n        return this.#handler?.onComplete?.(trailers)\n      }\n\n      onError (err) {\n        return this.#handler?.onError?.(err)\n      }\n    }\n    const Controller = class {\n      #controller = null\n      constructor (controller) {\n        this.#controller = controller\n      }\n\n      abort (reason) {\n        return this.#controller?.abort?.(reason)\n      }\n\n      resume () {\n        return this.#controller?.resume?.()\n      }\n\n      pause () {\n        return this.#controller?.pause?.()\n      }\n    }\n\n    describe('#onConnect', () => {\n      test('should delegate onConnect-method', t => {\n        t = tspl(t, { plan: 2 })\n        const handler = new Handler(\n          {\n            onConnect: (abort, ctx) => {\n              t.equal(typeof abort, 'function')\n              t.equal(typeof ctx, 'object')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onRequestStart(new Controller(), {})\n      })\n\n      test('should not throw if onConnect-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onRequestStart())\n      })\n    })\n\n    describe('#onHeaders', () => {\n      test('should delegate onHeaders-method', t => {\n        t = tspl(t, { plan: 4 })\n        const handler = new Handler(\n          {\n            onHeaders: (statusCode, headers, resume, statusMessage) => {\n              t.equal(statusCode, '200')\n              t.equal(`${headers[0].toString('utf-8')}: ${headers[1].toString('utf-8')}`, 'content-type: application/json')\n              t.equal(typeof resume, 'function')\n              t.equal(statusMessage, 'OK')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseStart(new Controller(), 200, {\n          'content-type': 'application/json'\n        }, 'OK')\n      })\n\n      test('should not throw if onHeaders-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseStart(new Controller(), 200, {\n          'content-type': 'application/json'\n        }))\n      })\n    })\n\n    describe('#onUpgrade', () => {\n      test('should delegate onUpgrade-method', t => {\n        t = tspl(t, { plan: 3 })\n        const handler = new Handler(\n          {\n            onUpgrade: (statusCode, headers, socket) => {\n              t.equal(statusCode, 301)\n              t.equal(`${headers[0].toString('utf-8')}: ${headers[1].toString('utf-8')}`, 'content-type: application/json')\n              t.equal(typeof socket, 'object')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onRequestUpgrade(new Controller(), 301, {\n          'content-type': 'application/json'\n        }, {})\n      })\n\n      test('should not throw if onUpgrade-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onRequestUpgrade(new Controller(), 301, {\n          'content-type': 'application/json'\n        }))\n      })\n    })\n\n    describe('#onData', () => {\n      test('should delegate onData-method', t => {\n        t = tspl(t, { plan: 1 })\n        const handler = new Handler(\n          {\n            onData: (chunk) => {\n              t.equal('chunk', chunk)\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseData(new Controller(), 'chunk')\n      })\n\n      test('should not throw if onData-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseData(new Controller(), 'chunk'))\n      })\n    })\n\n    describe('#onComplete', () => {\n      test('should delegate onComplete-method', t => {\n        t = tspl(t, { plan: 1 })\n        const handler = new Handler(\n          {\n            onComplete: (trailers) => {\n              t.equal(`${trailers[0].toString('utf-8')}: ${trailers[1].toString('utf-8')}`, 'x-trailer: trailer')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseEnd(new Controller(), { 'x-trailer': 'trailer' })\n      })\n\n      test('should not throw if onComplete-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseEnd(new Controller(), { 'x-trailer': 'trailer' }))\n      })\n    })\n\n    describe('#onError', () => {\n      test('should delegate onError-method', t => {\n        t = tspl(t, { plan: 1 })\n        const handler = new Handler(\n          {\n            onError: (err) => {\n              t.equal(err.message, 'Oops!')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseError(new Controller(), new Error('Oops!'))\n      })\n\n      test('should throw if onError-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.throws(() => decorator.onResponseError(new Controller(), new Error('Oops!')))\n      })\n    })\n  })\n\n  describe('no-wrap', () => {\n    const Handler = class {\n      #handler = null\n      constructor (handler) {\n        this.#handler = handler\n      }\n\n      onRequestStart (controller, context) {\n        return this.#handler?.onRequestStart?.(controller, context)\n      }\n\n      onRequestUpgrade (controller, statusCode, headers, socket) {\n        return this.#handler?.onRequestUpgrade?.(controller, statusCode, headers, socket)\n      }\n\n      onResponseStart (controller, statusCode, headers, statusMessage) {\n        return this.#handler?.onResponseStart?.(controller, statusCode, headers, statusMessage)\n      }\n\n      onResponseData (controller, data) {\n        return this.#handler?.onResponseData?.(controller, data)\n      }\n\n      onResponseEnd (controller, trailers) {\n        return this.#handler?.onResponseEnd?.(controller, trailers)\n      }\n\n      onResponseError (controller, err) {\n        return this.#handler?.onResponseError?.(controller, err)\n      }\n    }\n    const Controller = class {\n      #controller = null\n      constructor (controller) {\n        this.#controller = controller\n      }\n\n      abort (reason) {\n        return this.#controller?.abort?.(reason)\n      }\n\n      resume () {\n        return this.#controller?.resume?.()\n      }\n\n      pause () {\n        return this.#controller?.pause?.()\n      }\n    }\n\n    describe('#onRequestStart', () => {\n      test('should delegate onRequestStart-method', t => {\n        t = tspl(t, { plan: 2 })\n        const handler = new Handler(\n          {\n            onRequestStart: (controller, ctx) => {\n              t.equal(controller.constructor, Controller)\n              t.equal(typeof ctx, 'object')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onRequestStart(new Controller(), {})\n      })\n\n      test('should not throw if onRequestStart-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onRequestStart())\n      })\n    })\n\n    describe('#onRequestUpgrade', () => {\n      test('should delegate onRequestUpgrade-method', t => {\n        t = tspl(t, { plan: 4 })\n        const handler = new Handler(\n          {\n            onRequestUpgrade: (controller, statusCode, headers, socket) => {\n              t.equal(controller.constructor, Controller)\n              t.equal(statusCode, 301)\n              t.equal(headers['content-type'], 'application/json')\n              t.equal(typeof socket, 'object')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onRequestUpgrade(new Controller(), 301, {\n          'content-type': 'application/json'\n        }, {})\n      })\n\n      test('should not throw if onRequestUpgrade-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onRequestUpgrade(new Controller(), 301, {\n          'content-type': 'application/json'\n        }, {}))\n      })\n    })\n\n    describe('#onResponseStart', () => {\n      test('should delegate onResponseStart-method', t => {\n        t = tspl(t, { plan: 4 })\n        const handler = new Handler(\n          {\n            onResponseStart: (controller, statusCode, headers, message) => {\n              t.equal(controller.constructor, Controller)\n              t.equal(statusCode, 200)\n              t.equal(headers['content-type'], 'application/json')\n              t.equal(message, 'OK')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseStart(new Controller(), 200, {\n          'content-type': 'application/json'\n        }, 'OK')\n      })\n\n      test('should not throw if onResponseStart-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseStart(new Controller(), 200, {\n          'content-type': 'application/json'\n        }, 'OK'))\n      })\n    })\n\n    describe('#onResponseData', () => {\n      test('should delegate onResponseData-method', t => {\n        t = tspl(t, { plan: 2 })\n        const handler = new Handler(\n          {\n            onResponseData: (controller, chunk) => {\n              t.equal(controller.constructor, Controller)\n              t.equal('chunk', chunk)\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseData(new Controller(), 'chunk')\n      })\n\n      test('should not throw if onResponseData-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseData(new Controller(), 'chunk'))\n      })\n    })\n\n    describe('#onResponseEnd', () => {\n      test('should delegate onResponseEnd-method', t => {\n        t = tspl(t, { plan: 2 })\n        const handler = new Handler(\n          {\n            onResponseEnd: (controller, trailers) => {\n              t.equal(controller.constructor, Controller)\n              t.equal(trailers['x-trailer'], 'trailer')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseEnd(new Controller(), { 'x-trailer': 'trailer' })\n      })\n\n      test('should not throw if onResponseEnd-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({})\n        t.doesNotThrow(() => decorator.onResponseEnd(new Controller(), { 'x-trailer': 'trailer' }))\n      })\n    })\n\n    describe('#onResponseError', () => {\n      test('should delegate onError-method', t => {\n        t = tspl(t, { plan: 2 })\n        const handler = new Handler(\n          {\n            onResponseError: (controller, err) => {\n              t.equal(controller.constructor, Controller)\n              t.equal(err.message, 'Oops!')\n            }\n          })\n        const decorator = new DecoratorHandler(handler)\n        decorator.onResponseError(new Controller(), new Error('Oops!'))\n      })\n\n      test('should throw if onError-method is not defined in the handler', t => {\n        t = tspl(t, { plan: 1 })\n        const decorator = new DecoratorHandler({\n          // To hin and not wrap the instance\n          onRequestStart: () => {}\n        })\n        t.doesNotThrow(() => decorator.onResponseError(new Controller()))\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/dispatcher.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\n\nconst Dispatcher = require('../lib/dispatcher/dispatcher')\n\nclass PoorImplementation extends Dispatcher {}\n\ntest('dispatcher implementation', (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const dispatcher = new Dispatcher()\n  t.throws(() => dispatcher.dispatch(), Error, 'throws on unimplemented dispatch')\n  t.throws(() => dispatcher.close(), Error, 'throws on unimplemented close')\n  t.throws(() => dispatcher.destroy(), Error, 'throws on unimplemented destroy')\n\n  const poorImplementation = new PoorImplementation()\n  t.throws(() => poorImplementation.dispatch(), Error, 'throws on unimplemented dispatch')\n  t.throws(() => poorImplementation.close(), Error, 'throws on unimplemented close')\n  t.throws(() => poorImplementation.destroy(), Error, 'throws on unimplemented destroy')\n})\n\ntest('dispatcher.compose', (t) => {\n  t = tspl(t, { plan: 7 })\n\n  const dispatcher = new Dispatcher()\n  const interceptor = () => (opts, handler) => {}\n  // Should return a new dispatcher\n  t.ok(dispatcher.compose(interceptor) !== dispatcher)\n  t.throws(() => dispatcher.dispatch({}), Error, 'invalid interceptor')\n  t.throws(() => dispatcher.dispatch(() => null), Error, 'invalid interceptor')\n  t.throws(() => dispatcher.dispatch(dispatch => dispatch, () => () => {}, Error, 'invalid interceptor'))\n\n  const composed = dispatcher.compose(interceptor)\n  t.equal(typeof composed.dispatch, 'function', 'returns an object with a dispatch method')\n  t.equal(typeof composed.close, 'function', 'returns an object with a close method')\n  t.equal(typeof composed.destroy, 'function', 'returns an object with a destroy method')\n})\n"
  },
  {
    "path": "test/env-http-proxy-agent-nodejs-bundle.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test, after, before } = require('node:test')\nconst { EnvHttpProxyAgent, setGlobalDispatcher } = require('../index-fetch')\nconst http = require('node:http')\nconst net = require('node:net')\nconst { once } = require('node:events')\n\nconst env = { ...process.env }\n\ndescribe('EnvHttpProxyAgent and setGlobalDispatcher', () => {\n  before(() => {\n    ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy', 'NO_PROXY', 'no_proxy'].forEach((varname) => {\n      delete process.env[varname]\n    })\n  })\n\n  after(() => {\n    process.env = { ...env }\n  })\n\n  test('should work with global fetch from undici bundled with Node.js', async (t) => {\n    const { strictEqual } = tspl(t, { plan: 3 })\n\n    // Instead of using mocks, start a real server and a minimal proxy server\n    // in order to exercise the actual paths in EnvHttpProxyAgent from the\n    // Node.js bundle.\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end('Hello world') })\n    server.on('error', err => { console.log('Server error', err) })\n    server.listen(0)\n    await once(server, 'listening')\n    t.after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    const proxy = http.createServer({ joinDuplicateHeaders: true })\n    proxy.on('connect', (req, clientSocket, head) => {\n      // Check that the proxy is actually used to tunnel the request sent below.\n      const [hostname, port] = req.url.split(':')\n      strictEqual(hostname, 'localhost')\n      strictEqual(port, server.address().port.toString())\n\n      const serverSocket = net.connect(port, hostname, () => {\n        clientSocket.write(\n          'HTTP/1.1 200 Connection Established\\r\\n' +\n          'Proxy-agent: Node.js-Proxy\\r\\n' +\n          '\\r\\n'\n        )\n        serverSocket.write(head)\n        clientSocket.pipe(serverSocket)\n        serverSocket.pipe(clientSocket)\n      })\n\n      serverSocket.on('error', () => {\n        clientSocket.write('HTTP/1.1 500 Connection Error\\r\\n\\r\\n')\n        clientSocket.end()\n      })\n    })\n\n    proxy.on('error', (err) => { console.log('Proxy error', err) })\n\n    proxy.listen(0)\n    await once(proxy, 'listening')\n    t.after(() => {\n      proxy.closeAllConnections?.()\n      proxy.close()\n    })\n\n    // Use setGlobalDispatcher and EnvHttpProxyAgent from Node.js\n    // and make sure that they work together.\n    const proxyAddress = `http://localhost:${proxy.address().port}`\n    const serverAddress = `http://localhost:${server.address().port}`\n    process.env.http_proxy = proxyAddress\n    setGlobalDispatcher(new EnvHttpProxyAgent())\n\n    // eslint-disable-next-line no-restricted-globals\n    const res = await fetch(serverAddress)\n    strictEqual(await res.text(), 'Hello world')\n  })\n})\n"
  },
  {
    "path": "test/env-http-proxy-agent.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, describe, after, beforeEach } = require('node:test')\nconst { EnvHttpProxyAgent, ProxyAgent, Agent, fetch, MockAgent } = require('..')\nconst { kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent, kClosed, kDestroyed, kProxy } = require('../lib/core/symbols')\n\nconst env = { ...process.env }\n\nbeforeEach(() => {\n  ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy', 'NO_PROXY', 'no_proxy'].forEach((varname) => {\n    delete process.env[varname]\n  })\n})\n\nafter(() => {\n  process.env = { ...env }\n})\n\ntest('does not create any proxy agents if http_proxy and https_proxy are not set', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const dispatcher = new EnvHttpProxyAgent()\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.deepStrictEqual(dispatcher[kHttpProxyAgent], dispatcher[kNoProxyAgent])\n  t.deepStrictEqual(dispatcher[kHttpsProxyAgent], dispatcher[kNoProxyAgent])\n  return dispatcher.close()\n})\n\ntest('creates one proxy agent for both http and https when only http_proxy is defined', async (t) => {\n  t = tspl(t, { plan: 5 })\n  process.env.http_proxy = 'http://example.com:8080'\n  const dispatcher = new EnvHttpProxyAgent()\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.ok(dispatcher[kHttpProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://example.com:8080/')\n  t.deepStrictEqual(dispatcher[kHttpsProxyAgent], dispatcher[kHttpProxyAgent])\n  return dispatcher.close()\n})\n\ntest('creates separate proxy agent for http and https when http_proxy and https_proxy are set', async (t) => {\n  t = tspl(t, { plan: 6 })\n  process.env.http_proxy = 'http://example.com:8080'\n  process.env.https_proxy = 'http://example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.ok(dispatcher[kHttpProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://example.com:8080/')\n  t.ok(dispatcher[kHttpsProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('handles uppercase HTTP_PROXY and HTTPS_PROXY', async (t) => {\n  t = tspl(t, { plan: 6 })\n  process.env.HTTP_PROXY = 'http://example.com:8080'\n  process.env.HTTPS_PROXY = 'http://example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.ok(dispatcher[kHttpProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://example.com:8080/')\n  t.ok(dispatcher[kHttpsProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('accepts httpProxy and httpsProxy options', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const opts = {\n    httpProxy: 'http://example.com:8080',\n    httpsProxy: 'http://example.com:8443'\n  }\n  const dispatcher = new EnvHttpProxyAgent(opts)\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.ok(dispatcher[kHttpProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://example.com:8080/')\n  t.ok(dispatcher[kHttpsProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('prefers options over env vars', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const opts = {\n    httpProxy: 'http://opts.example.com:8080',\n    httpsProxy: 'http://opts.example.com:8443'\n  }\n  process.env.http_proxy = 'http://lower.example.com:8080'\n  process.env.https_proxy = 'http://lower.example.com:8443'\n  process.env.HTTP_PROXY = 'http://upper.example.com:8080'\n  process.env.HTTPS_PROXY = 'http://upper.example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent(opts)\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://opts.example.com:8080/')\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://opts.example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('prefers lowercase over uppercase env vars', async (t) => {\n  t = tspl(t, { plan: 2 })\n  process.env.HTTP_PROXY = 'http://upper.example.com:8080'\n  process.env.HTTPS_PROXY = 'http://upper.example.com:8443'\n  process.env.http_proxy = 'http://lower.example.com:8080'\n  process.env.https_proxy = 'http://lower.example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  t.equal(dispatcher[kHttpProxyAgent][kProxy].uri, 'http://lower.example.com:8080/')\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://lower.example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('prefers lowercase over uppercase env vars even when empty', async (t) => {\n  t = tspl(t, { plan: 2 })\n  process.env.HTTP_PROXY = 'http://upper.example.com:8080'\n  process.env.HTTP_PROXY = 'http://upper.example.com:8443'\n  process.env.http_proxy = ''\n  process.env.https_proxy = ''\n  const dispatcher = new EnvHttpProxyAgent()\n\n  t.deepStrictEqual(dispatcher[kHttpProxyAgent], dispatcher[kNoProxyAgent])\n  t.deepStrictEqual(dispatcher[kHttpsProxyAgent], dispatcher[kNoProxyAgent])\n  return dispatcher.close()\n})\n\ntest('creates a proxy agent only for https when only https_proxy is set', async (t) => {\n  t = tspl(t, { plan: 5 })\n  process.env.https_proxy = 'http://example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  t.ok(dispatcher[kNoProxyAgent] instanceof Agent)\n  t.ok(!(dispatcher[kNoProxyAgent] instanceof ProxyAgent))\n  t.deepStrictEqual(dispatcher[kHttpProxyAgent], dispatcher[kNoProxyAgent])\n  t.ok(dispatcher[kHttpsProxyAgent] instanceof ProxyAgent)\n  t.equal(dispatcher[kHttpsProxyAgent][kProxy].uri, 'http://example.com:8443/')\n  return dispatcher.close()\n})\n\ntest('closes all agents', async (t) => {\n  t = tspl(t, { plan: 3 })\n  process.env.http_proxy = 'http://example.com:8080'\n  process.env.https_proxy = 'http://example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  await dispatcher.close()\n  t.ok(dispatcher[kNoProxyAgent][kClosed])\n  t.ok(dispatcher[kHttpProxyAgent][kClosed])\n  t.ok(dispatcher[kHttpsProxyAgent][kClosed])\n})\n\ntest('destroys all agents', async (t) => {\n  t = tspl(t, { plan: 3 })\n  process.env.http_proxy = 'http://example.com:8080'\n  process.env.https_proxy = 'http://example.com:8443'\n  const dispatcher = new EnvHttpProxyAgent()\n  await dispatcher.destroy()\n  t.ok(dispatcher[kNoProxyAgent][kDestroyed])\n  t.ok(dispatcher[kHttpProxyAgent][kDestroyed])\n  t.ok(dispatcher[kHttpsProxyAgent][kDestroyed])\n})\n\nconst createEnvHttpProxyAgentWithMocks = (plan = 1, opts = {}) => {\n  const factory = (origin) => {\n    const mockAgent = new MockAgent()\n    const mockPool = mockAgent.get(origin)\n    let i = 0\n    while (i < plan) {\n      mockPool.intercept({ path: /.*/ }).reply(200, 'OK')\n      i++\n    }\n    return mockPool\n  }\n  process.env.http_proxy = 'http://localhost:8080'\n  process.env.https_proxy = 'http://localhost:8443'\n  const dispatcher = new EnvHttpProxyAgent({ ...opts, factory })\n  const agentSymbols = [kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent]\n  agentSymbols.forEach((agentSymbol) => {\n    const originalDispatch = dispatcher[agentSymbol].dispatch\n    dispatcher[agentSymbol].dispatch = function () {\n      dispatcher[agentSymbol].dispatch.called = true\n      return originalDispatch.apply(this, arguments)\n    }\n    dispatcher[agentSymbol].dispatch.called = false\n  })\n  const usesProxyAgent = async (agent, url) => {\n    await fetch(url, { dispatcher })\n    const result = agentSymbols.every((agentSymbol) => agent === agentSymbol\n      ? dispatcher[agentSymbol].dispatch.called === true\n      : dispatcher[agentSymbol].dispatch.called === false)\n\n    agentSymbols.forEach((agentSymbol) => {\n      dispatcher[agentSymbol].dispatch.called = false\n    })\n    return result\n  }\n  const doesNotProxy = usesProxyAgent.bind(this, kNoProxyAgent)\n  return {\n    dispatcher,\n    doesNotProxy,\n    usesProxyAgent\n  }\n}\n\ntest('uses the appropriate proxy for the protocol', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n  t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com/'))\n  t.ok(await usesProxyAgent(kHttpsProxyAgent, 'https://example.com/'))\n  return dispatcher.close()\n})\n\ndescribe('no_proxy', () => {\n  test('set to *', async (t) => {\n    t = tspl(t, { plan: 2 })\n    process.env.no_proxy = '*'\n    const { dispatcher, doesNotProxy } = createEnvHttpProxyAgentWithMocks(2)\n    t.ok(await doesNotProxy('https://example.com'))\n    t.ok(await doesNotProxy('http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('set but empty', async (t) => {\n    t = tspl(t, { plan: 1 })\n    process.env.no_proxy = ''\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('no entries (comma)', async (t) => {\n    t = tspl(t, { plan: 1 })\n    process.env.no_proxy = ','\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('no entries (whitespace)', async (t) => {\n    t = tspl(t, { plan: 1 })\n    process.env.no_proxy = ' '\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('no entries (multiple whitespace / commas)', async (t) => {\n    t = tspl(t, { plan: 1 })\n    process.env.no_proxy = ',\\t,,,\\n,  ,\\r'\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('single host', async (t) => {\n    t = tspl(t, { plan: 9 })\n    process.env.no_proxy = 'example'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(9)\n    t.ok(await doesNotProxy('http://example'))\n    t.ok(await doesNotProxy('http://example:80'))\n    t.ok(await doesNotProxy('http://example:0'))\n    t.ok(await doesNotProxy('http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.no'))\n    t.ok(await doesNotProxy('http://a.b.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://host/example'))\n    return dispatcher.close()\n  })\n\n  test('as an option', async (t) => {\n    t = tspl(t, { plan: 9 })\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(9, { noProxy: 'example' })\n    t.ok(await doesNotProxy('http://example'))\n    t.ok(await doesNotProxy('http://example:80'))\n    t.ok(await doesNotProxy('http://example:0'))\n    t.ok(await doesNotProxy('http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.no'))\n    t.ok(await doesNotProxy('http://a.b.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://host/example'))\n    return dispatcher.close()\n  })\n\n  test('subdomain', async (t) => {\n    t = tspl(t, { plan: 8 })\n    process.env.no_proxy = 'sub.example'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(8)\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example:80'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example:0'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await doesNotProxy('http://no.sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://sub-example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.sub'))\n    return dispatcher.close()\n  })\n\n  test('host + port', async (t) => {\n    t = tspl(t, { plan: 13 })\n    process.env.no_proxy = 'example:80, localhost:3000'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(13)\n    t.ok(await doesNotProxy('http://example'))\n    t.ok(await doesNotProxy('http://example:80'))\n    t.ok(await doesNotProxy('http://sub.example:80'))\n    t.ok(await doesNotProxy('http://example:0'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.no'))\n    t.ok(await doesNotProxy('http://a.b.example'))\n    t.ok(await doesNotProxy('http://localhost:3000/'))\n    t.ok(await doesNotProxy('https://localhost:3000/'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://localhost:3001/'))\n    t.ok(await usesProxyAgent(kHttpsProxyAgent, 'https://localhost:3001/'))\n    return dispatcher.close()\n  })\n\n  test('host suffix - leading dot stripped', async (t) => {\n    t = tspl(t, { plan: 9 })\n    process.env.no_proxy = '.example'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(9)\n    t.ok(await doesNotProxy('http://example'))\n    t.ok(await doesNotProxy('http://example:80'))\n    t.ok(await doesNotProxy('http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await doesNotProxy('http://sub.example:80'))\n    t.ok(await doesNotProxy('http://sub.example:1337'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.no'))\n    t.ok(await doesNotProxy('http://a.b.example'))\n    return dispatcher.close()\n  })\n\n  test('host suffix with *. - leading dot with asterisk stripped', async (t) => {\n    t = tspl(t, { plan: 9 })\n    process.env.no_proxy = '*.example'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(9)\n    t.ok(await doesNotProxy('http://example'))\n    t.ok(await doesNotProxy('http://example:80'))\n    t.ok(await doesNotProxy('http://example:1337'))\n    t.ok(await doesNotProxy('http://sub.example'))\n    t.ok(await doesNotProxy('http://sub.example:80'))\n    t.ok(await doesNotProxy('http://sub.example:1337'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.no'))\n    t.ok(await doesNotProxy('http://a.b.example'))\n    return dispatcher.close()\n  })\n\n  test('substring suffix are NOT supported', async (t) => {\n    t = tspl(t, { plan: 6 })\n    process.env.no_proxy = '*example'\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(6)\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://x.prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://a.b.example'))\n    return dispatcher.close()\n  })\n\n  test('arbitrary wildcards are NOT supported', async (t) => {\n    t = tspl(t, { plan: 6 })\n    process.env.no_proxy = '.*example'\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(6)\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://sub.example'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://x.prefexample'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://a.b.example'))\n    return dispatcher.close()\n  })\n\n  test('IP addresses', async (t) => {\n    t = tspl(t, { plan: 12 })\n    process.env.no_proxy = '[::1],[::2]:80,10.0.0.1,10.0.0.2:80'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(12)\n    t.ok(await doesNotProxy('http://[::1]/'))\n    t.ok(await doesNotProxy('http://[::1]:80/'))\n    t.ok(await doesNotProxy('http://[::1]:1337/'))\n    t.ok(await doesNotProxy('http://[::2]/'))\n    t.ok(await doesNotProxy('http://[::2]:80/'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://[::2]:1337/'))\n    t.ok(await doesNotProxy('http://10.0.0.1/'))\n    t.ok(await doesNotProxy('http://10.0.0.1:80/'))\n    t.ok(await doesNotProxy('http://10.0.0.1:1337/'))\n    t.ok(await doesNotProxy('http://10.0.0.2/'))\n    t.ok(await doesNotProxy('http://10.0.0.2:80/'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://10.0.0.2:1337/'))\n    return dispatcher.close()\n  })\n\n  test('CIDR is NOT supported', async (t) => {\n    t = tspl(t, { plan: 2 })\n    process.env.no_proxy = '127.0.0.1/32'\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(2)\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://127.0.0.1'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://127.0.0.1/32'))\n    return dispatcher.close()\n  })\n\n  test('127.0.0.1 does NOT match localhost', async (t) => {\n    t = tspl(t, { plan: 2 })\n    process.env.no_proxy = '127.0.0.1'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(2)\n    t.ok(await doesNotProxy('http://127.0.0.1'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://localhost'))\n    return dispatcher.close()\n  })\n\n  test('protocols that have a default port', async (t) => {\n    t = tspl(t, { plan: 6 })\n    process.env.no_proxy = 'xxx:21,xxx:70,xxx:80,xxx:443'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(6)\n    t.ok(await doesNotProxy('http://xxx'))\n    t.ok(await doesNotProxy('http://xxx:80'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://xxx:1337'))\n    t.ok(await doesNotProxy('https://xxx'))\n    t.ok(await doesNotProxy('https://xxx:443'))\n    t.ok(await usesProxyAgent(kHttpsProxyAgent, 'https://xxx:1337'))\n    return dispatcher.close()\n  })\n\n  test('should not be case sensitive', async (t) => {\n    t = tspl(t, { plan: 6 })\n    process.env.NO_PROXY = 'XXX YYY ZzZ'\n    const { dispatcher, doesNotProxy } = createEnvHttpProxyAgentWithMocks(6)\n    t.ok(await doesNotProxy('http://xxx'))\n    t.ok(await doesNotProxy('http://XXX'))\n    t.ok(await doesNotProxy('http://yyy'))\n    t.ok(await doesNotProxy('http://YYY'))\n    t.ok(await doesNotProxy('http://ZzZ'))\n    t.ok(await doesNotProxy('http://zZz'))\n    return dispatcher.close()\n  })\n\n  test('prefers lowercase over uppercase', async (t) => {\n    t = tspl(t, { plan: 2 })\n    process.env.NO_PROXY = 'another.com'\n    process.env.no_proxy = 'example.com'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(6)\n    t.ok(await doesNotProxy('http://example.com'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://another.com'))\n    return dispatcher.close()\n  })\n\n  test('prefers lowercase over uppercase even when it is empty', async (t) => {\n    t = tspl(t, { plan: 1 })\n    process.env.NO_PROXY = 'example.com'\n    process.env.no_proxy = ''\n    const { dispatcher, usesProxyAgent } = createEnvHttpProxyAgentWithMocks()\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    return dispatcher.close()\n  })\n\n  test('handles env var changes', async (t) => {\n    t = tspl(t, { plan: 4 })\n    process.env.no_proxy = 'example.com'\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(4)\n    t.ok(await doesNotProxy('http://example.com'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://another.com'))\n    process.env.no_proxy = 'another.com'\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://example.com'))\n    t.ok(await doesNotProxy('http://another.com'))\n    return dispatcher.close()\n  })\n\n  test('ignores env var changes when set via config', async (t) => {\n    t = tspl(t, { plan: 4 })\n    const { dispatcher, doesNotProxy, usesProxyAgent } = createEnvHttpProxyAgentWithMocks(4, { noProxy: 'example.com' })\n    t.ok(await doesNotProxy('http://example.com'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://another.com'))\n    process.env.no_proxy = 'another.com'\n    t.ok(await doesNotProxy('http://example.com'))\n    t.ok(await usesProxyAgent(kHttpProxyAgent, 'http://another.com'))\n    return dispatcher.close()\n  })\n})\n"
  },
  {
    "path": "test/errors.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test } = require('node:test')\n\nconst errors = require('../lib/core/errors')\n\nconst createScenario = (ErrorClass, defaultMessage, name, code) => ({\n  ErrorClass,\n  defaultMessage,\n  name,\n  code\n})\n\nconst scenarios = [\n  createScenario(errors.UndiciError, '', 'UndiciError', 'UND_ERR'),\n  createScenario(errors.ConnectTimeoutError, 'Connect Timeout Error', 'ConnectTimeoutError', 'UND_ERR_CONNECT_TIMEOUT'),\n  createScenario(errors.HeadersTimeoutError, 'Headers Timeout Error', 'HeadersTimeoutError', 'UND_ERR_HEADERS_TIMEOUT'),\n  createScenario(errors.HeadersOverflowError, 'Headers Overflow Error', 'HeadersOverflowError', 'UND_ERR_HEADERS_OVERFLOW'),\n  createScenario(errors.InvalidArgumentError, 'Invalid Argument Error', 'InvalidArgumentError', 'UND_ERR_INVALID_ARG'),\n  createScenario(errors.InvalidReturnValueError, 'Invalid Return Value Error', 'InvalidReturnValueError', 'UND_ERR_INVALID_RETURN_VALUE'),\n  createScenario(errors.RequestAbortedError, 'Request aborted', 'AbortError', 'UND_ERR_ABORTED'),\n  createScenario(errors.InformationalError, 'Request information', 'InformationalError', 'UND_ERR_INFO'),\n  createScenario(errors.RequestContentLengthMismatchError, 'Request body length does not match content-length header', 'RequestContentLengthMismatchError', 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'),\n  createScenario(errors.ClientDestroyedError, 'The client is destroyed', 'ClientDestroyedError', 'UND_ERR_DESTROYED'),\n  createScenario(errors.ClientClosedError, 'The client is closed', 'ClientClosedError', 'UND_ERR_CLOSED'),\n  createScenario(errors.SocketError, 'Socket error', 'SocketError', 'UND_ERR_SOCKET'),\n  createScenario(errors.NotSupportedError, 'Not supported error', 'NotSupportedError', 'UND_ERR_NOT_SUPPORTED'),\n  createScenario(errors.ResponseContentLengthMismatchError, 'Response body length does not match content-length header', 'ResponseContentLengthMismatchError', 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'),\n  createScenario(errors.ResponseExceededMaxSizeError, 'Response content exceeded max size', 'ResponseExceededMaxSizeError', 'UND_ERR_RES_EXCEEDED_MAX_SIZE')\n]\n\nscenarios.forEach(scenario => {\n  describe(scenario.name, () => {\n    const SAMPLE_MESSAGE = 'sample message'\n\n    const errorWithDefaultMessage = () => new scenario.ErrorClass()\n    const errorWithProvidedMessage = () => new scenario.ErrorClass(SAMPLE_MESSAGE)\n\n    test('should use default message', t => {\n      t = tspl(t, { plan: 1 })\n\n      const error = errorWithDefaultMessage()\n\n      t.strictEqual(error.message, scenario.defaultMessage)\n    })\n\n    test('should use provided message', t => {\n      t = tspl(t, { plan: 1 })\n\n      const error = errorWithProvidedMessage()\n\n      t.strictEqual(error.message, SAMPLE_MESSAGE)\n    })\n\n    test('should have proper fields', t => {\n      t = tspl(t, { plan: 6 })\n      const errorInstances = [errorWithDefaultMessage(), errorWithProvidedMessage()]\n      errorInstances.forEach(error => {\n        t.strictEqual(error.name, scenario.name)\n        t.strictEqual(error.code, scenario.code)\n        t.ok(error.stack)\n      })\n    })\n  })\n})\n\ndescribe('Default HTTPParseError Codes', () => {\n  test('code and data should be undefined when not set', t => {\n    t = tspl(t, { plan: 2 })\n\n    const error = new errors.HTTPParserError('HTTPParserError')\n\n    t.strictEqual(error.code, undefined)\n    t.strictEqual(error.data, undefined)\n  })\n})\n"
  },
  {
    "path": "test/esm-wrapper.js",
    "content": "'use strict'\n\n;(async () => {\n  try {\n    await import('./utils/esm-wrapper.mjs')\n  } catch (e) {\n    if (e.message === 'Not supported') {\n      require('node:test') // shows skipped\n      return\n    }\n    console.error(e.stack)\n    process.exitCode = 1\n  }\n})()\n"
  },
  {
    "path": "test/eventsource/eventsource-attributes.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe, before, after } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - eventhandler idl', () => {\n  let server\n  let port\n\n  before(async () => {\n    server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'dummy')\n    })\n\n    await once(server.listen(0), 'listening')\n    port = server.address().port\n  })\n\n  after(() => { server.close() })\n\n  const eventhandlerIdl = ['onmessage', 'onerror', 'onopen']\n\n  eventhandlerIdl.forEach((type) => {\n    test(`Should properly configure the ${type} eventhandler idl`, (t) => {\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n\n      // Eventsource eventhandler idl is by default null,\n      t.assert.strictEqual(eventSourceInstance[type], null)\n\n      // The eventhandler idl is by default not enumerable.\n      t.assert.strictEqual(Object.prototype.propertyIsEnumerable.call(eventSourceInstance, type), false)\n\n      // The eventhandler idl ignores non-functions.\n      eventSourceInstance[type] = 7\n      t.assert.strictEqual(EventSource[type], undefined)\n\n      // The eventhandler idl accepts functions.\n      function fn () {\n        t.assert.fail('Should not have called the eventhandler')\n      }\n      eventSourceInstance[type] = fn\n      t.assert.strictEqual(eventSourceInstance[type], fn)\n\n      // The eventhandler idl can be set to another function.\n      function fn2 () { }\n      eventSourceInstance[type] = fn2\n      t.assert.strictEqual(eventSourceInstance[type], fn2)\n\n      // The eventhandler idl overrides the previous function.\n      eventSourceInstance.dispatchEvent(new Event(type))\n\n      eventSourceInstance.close()\n    })\n  })\n})\n\ndescribe('EventSource - constants', () => {\n  [\n    ['CONNECTING', 0],\n    ['OPEN', 1],\n    ['CLOSED', 2]\n  ].forEach((config) => {\n    test(`Should expose the ${config[0]} constant`, (t) => {\n      const [constant, value] = config\n\n      // EventSource exposes the constant.\n      t.assert.strictEqual(Object.hasOwn(EventSource, constant), true)\n\n      // The value is properly set.\n      t.assert.strictEqual(EventSource[constant], value)\n\n      // The constant is enumerable.\n      t.assert.strictEqual(Object.prototype.propertyIsEnumerable.call(EventSource, constant), true)\n\n      // The constant is not writable.\n      try {\n        EventSource[constant] = 666\n      } catch (e) {\n        t.assert.strictEqual(e instanceof TypeError, true)\n      }\n      // The constant is not configurable.\n      try {\n        delete EventSource[constant]\n      } catch (e) {\n        t.assert.strictEqual(e instanceof TypeError, true)\n      }\n      t.assert.strictEqual(EventSource[constant], value)\n    })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-close.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe, after } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - close', () => {\n  test('should not emit error when closing the EventSource Instance', async (t) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.connection, 'keep-alive')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('data: hello\\n\\n')\n\n      res.on('close', () => {\n        server.close()\n      })\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = () => {\n      eventSourceInstance.close()\n    }\n\n    eventSourceInstance.onerror = () => {\n      t.assert.fail('Should not have errored')\n    }\n\n    await once(server, 'close')\n  })\n\n  test('should set readyState to CLOSED', async (t) => {\n    t.plan(3)\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.connection, 'keep-alive')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('data: hello\\n\\n')\n    })\n    after(() => server.close())\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = () => {\n      t.assert.strictEqual(eventSourceInstance.readyState, EventSource.OPEN)\n      eventSourceInstance.close()\n      t.assert.strictEqual(eventSourceInstance.readyState, EventSource.CLOSED)\n      server.close()\n    }\n\n    eventSourceInstance.onerror = () => {\n      t.assert.fail('Should not have errored')\n    }\n    await once(server, 'close')\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-connect.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe, after } = require('node:test')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { EventSource, defaultReconnectionTime } = require('../../lib/web/eventsource/eventsource')\nconst { randomInt } = require('node:crypto')\n\ndescribe('EventSource - sending correct request headers', () => {\n  test('should send request with connection keep-alive', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.connection, 'keep-alive')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should send request with sec-fetch-mode set to cors', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers['sec-fetch-mode'], 'cors')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should send request with pragma and cache-control set to no-cache', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers['cache-control'], 'no-cache')\n      t.assert.strictEqual(req.headers.pragma, 'no-cache')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should send request with accept text/event-stream', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.accept, 'text/event-stream')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n})\n\ndescribe('EventSource - received response must have content-type to be text/event-stream', () => {\n  test('should send request with accept text/event-stream', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should send request with accept text/event-stream;', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream;' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should handle content-type text/event-stream;charset=UTF-8 properly', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream;charset=UTF-8' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('should throw if content-type is text/html properly', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/html' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = (t) => {\n      t.assert.fail('Should not have opened')\n    }\n\n    eventSourceInstance.onerror = (t) => {\n      eventSourceInstance.close()\n      server.close()\n    }\n  })\n\n  test('should try to connect again if server is unreachable', async (t) => {\n    const clock = FakeTimers.install()\n\n    after(() => clock.uninstall())\n    const reconnectionTime = defaultReconnectionTime\n    const domain = 'bad.n' + randomInt(1e10).toString(36) + '.proxy'\n\n    const eventSourceInstance = new EventSource(`http://${domain}`)\n\n    const onerrorCalls = []\n    eventSourceInstance.onerror = (error) => {\n      onerrorCalls.push(error)\n    }\n    clock.tick(reconnectionTime)\n\n    await once(eventSourceInstance, 'error')\n\n    const start = Date.now()\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    const end = Date.now()\n\n    eventSourceInstance.close()\n\n    t.assert.strictEqual(onerrorCalls.length, 4, 'Expected 4 error events')\n    t.assert.strictEqual(end - start, 3 * reconnectionTime, `Expected reconnection to happen after ${3 * reconnectionTime}ms, but took ${end - start}ms`)\n  })\n\n  test('should try to connect again if server is unreachable, configure reconnectionTime', async (t) => {\n    const reconnectionTime = 1000\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    const domain = 'bad.n' + randomInt(1e10).toString(36) + '.proxy'\n\n    const eventSourceInstance = new EventSource(`http://${domain}`, {\n      node: {\n        reconnectionTime\n      }\n    })\n\n    const onerrorCalls = []\n    eventSourceInstance.onerror = (error) => {\n      onerrorCalls.push(error)\n    }\n\n    await once(eventSourceInstance, 'error')\n\n    const start = Date.now()\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    clock.tick(reconnectionTime)\n    await once(eventSourceInstance, 'error')\n    const end = Date.now()\n\n    eventSourceInstance.close()\n\n    t.assert.strictEqual(onerrorCalls.length, 4, 'Expected 4 error events')\n    t.assert.strictEqual(end - start, 3 * reconnectionTime, `Expected reconnection to happen after ${3 * reconnectionTime}ms, but took ${end - start}ms`)\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-constructor-stringify.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - constructor stringify', () => {\n  test('should stringify argument', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.connection, 'keep-alive')\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource({ toString: function () { return `http://localhost:${port}` } })\n    eventSourceInstance.onopen = () => {\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = () => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-constructor.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - withCredentials', () => {\n  test('withCredentials should be false by default', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n    eventSourceInstance.onopen = () => {\n      t.assert.strictEqual(eventSourceInstance.withCredentials, false)\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = () => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n\n  test('withCredentials can be set to true', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`, { withCredentials: true })\n    eventSourceInstance.onopen = () => {\n      t.assert.strictEqual(eventSourceInstance.withCredentials, true)\n      eventSourceInstance.close()\n      server.close()\n    }\n\n    eventSourceInstance.onerror = () => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-custom-dispatcher.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { Agent, EventSource } = require('../..')\nconst { test } = require('node:test')\n\ntest('EventSource allows setting custom dispatcher.', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n    t.assert.deepStrictEqual(req.headers['x-customer-header'], 'hello world')\n\n    res.end()\n    done()\n  })\n\n  t.after(() => {\n    server.close()\n  })\n\n  server.listen(0, () => {\n    class CustomHeaderAgent extends Agent {\n      dispatch (opts) {\n        opts.headers['x-customer-header'] = 'hello world'\n        return super.dispatch(...arguments)\n      }\n    }\n\n    const eventSourceInstance = new EventSource(`http://localhost:${server.address().port}`, {\n      dispatcher: new CustomHeaderAgent()\n    })\n    t.after(() => {\n      eventSourceInstance.close()\n    })\n  })\n})\n\ntest('EventSource allows setting custom dispatcher in EventSourceDict.', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n    t.assert.deepStrictEqual(req.headers['x-customer-header'], 'hello world')\n\n    res.end()\n\n    done()\n  })\n\n  t.after(() => {\n    server.close()\n  })\n\n  server.listen(0, () => {\n    class CustomHeaderAgent extends Agent {\n      dispatch (opts) {\n        opts.headers['x-customer-header'] = 'hello world'\n        return super.dispatch(...arguments)\n      }\n    }\n\n    const eventSourceInstance = new EventSource(`http://localhost:${server.address().port}`, {\n      node: {\n        dispatcher: new CustomHeaderAgent()\n      }\n    })\n    t.after(() => {\n      eventSourceInstance.close()\n    })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-message.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe, after } = require('node:test')\nconst { EventSource, defaultReconnectionTime } = require('../../lib/web/eventsource/eventsource')\nconst FakeTimers = require('@sinonjs/fake-timers')\n\ndescribe('EventSource - message', () => {\n  test('Should not emit a message if only retry field was sent', (t, done) => {\n    t.plan(2)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('retry: 100\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const start = Date.now()\n      let connectionCount = 0\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.onopen = () => {\n        if (++connectionCount === 2) {\n          t.assert.ok(Date.now() - start >= 100)\n          t.assert.ok(Date.now() - start < 1000)\n          eventSourceInstance.close()\n          done()\n        }\n      }\n      eventSourceInstance.onmessage = () => {\n        t.assert.fail('Should not have received a message')\n        eventSourceInstance.close()\n      }\n    })\n  })\n\n  test('Should not emit a message if no data is provided', async (t) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('event:message\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n\n    eventSourceInstance.onmessage = () => {\n      t.assert.fail('Should not have received a message')\n      eventSourceInstance.close()\n    }\n\n    eventSourceInstance.close()\n    t.assert.ok('Should not have received a message')\n  })\n\n  test('Should emit a custom type message if data is provided', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('event:custom\\ndata:test\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('custom', () => {\n        t.assert.ok(true)\n        done()\n        eventSourceInstance.close()\n      })\n    })\n  })\n\n  test('Should emit a message event if data is provided', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('data:test\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('message', () => {\n        t.assert.ok(true)\n        eventSourceInstance.close()\n        done()\n      })\n    })\n  })\n\n  test('Should emit a message event if data as a field is provided', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('data\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('message', () => {\n        t.assert.ok(true)\n        eventSourceInstance.close()\n        done()\n      })\n    })\n  })\n\n  test('Should emit a custom message event if data is empty', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('event:custom\\ndata:\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('custom', () => {\n        t.assert.ok(true)\n        eventSourceInstance.close()\n        done()\n      })\n    })\n  })\n\n  test('Should emit a message event if data is empty', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('data:\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('message', () => {\n        t.assert.ok(true)\n        eventSourceInstance.close()\n        done()\n      })\n    })\n  })\n\n  test('Should emit a custom message event if data only as a field is provided', (t, done) => {\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('event:custom\\ndata\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    after(() => server.close())\n\n    server.listen(0, () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.addEventListener('custom', () => {\n        t.assert.ok(true)\n        eventSourceInstance.close()\n        done()\n      })\n    })\n  })\n\n  test('Should not emit a custom type message if no data is provided', (t, done) => {\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    t.plan(1)\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('event:custom\\n\\n')\n      setTimeout(() => res.end(), 100)\n    })\n\n    let reconnectionCount = 0\n    after(() => server.close())\n\n    server.listen(0, async () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.onopen = () => {\n        if (++reconnectionCount === 2) {\n          t.assert.ok(true)\n          eventSourceInstance.close()\n          done()\n        }\n      }\n      eventSourceInstance.addEventListener('custom', () => {\n        t.assert.fail('Should not have received a message')\n        eventSourceInstance.close()\n      })\n\n      await once(eventSourceInstance, 'open')\n      clock.tick(defaultReconnectionTime)\n      await once(eventSourceInstance, 'error')\n\n      clock.tick(defaultReconnectionTime)\n      await once(eventSourceInstance, 'open')\n      clock.tick(defaultReconnectionTime)\n    })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-properties.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { EventSource } = require('../..') // assuming the test is in test/eventsource/\n\ntest('EventSource.prototype properties are configured correctly', (t) => {\n  const props = Object.entries(Object.getOwnPropertyDescriptors(EventSource.prototype))\n\n  for (const [key, value] of props) {\n    if (key !== 'constructor') {\n      t.assert.ok(value.enumerable, `${key} is not enumerable`)\n    }\n  }\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-reconnect.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe, after } = require('node:test')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { EventSource, defaultReconnectionTime } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - reconnect', () => {\n  test('Should reconnect on connection closed by server', (t, done) => {\n    t.plan(1)\n\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n    after(() => server.close())\n\n    server.listen(0, async () => {\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      let connectionCount = 0\n      eventSourceInstance.onopen = () => {\n        if (++connectionCount === 2) {\n          eventSourceInstance.close()\n          t.assert.ok(true)\n          done()\n        }\n      }\n\n      await once(eventSourceInstance, 'open')\n\n      clock.tick(10)\n      await once(eventSourceInstance, 'error')\n\n      clock.tick(defaultReconnectionTime)\n    })\n  })\n\n  test('Should reconnect on with reconnection timeout', (t, done) => {\n    t.plan(2)\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.end()\n    })\n    after(() => server.close())\n\n    server.listen(0, async () => {\n      const port = server.address().port\n\n      const start = Date.now()\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n\n      let connectionCount = 0\n      eventSourceInstance.onopen = () => {\n        if (++connectionCount === 2) {\n          t.assert.ok(Date.now() - start >= defaultReconnectionTime)\n          eventSourceInstance.close()\n          t.assert.ok(true)\n\n          done()\n        }\n      }\n\n      await once(eventSourceInstance, 'open')\n\n      clock.tick(10)\n      await once(eventSourceInstance, 'error')\n\n      clock.tick(defaultReconnectionTime)\n    })\n  })\n\n  test('Should reconnect on with modified reconnection timeout', (t, done) => {\n    t.plan(3)\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('retry: 100\\n\\n')\n      res.end()\n    })\n    after(() => server.close())\n\n    server.listen(0, async () => {\n      const port = server.address().port\n\n      const start = Date.now()\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n\n      let connectionCount = 0\n      eventSourceInstance.onopen = () => {\n        if (++connectionCount === 2) {\n          t.assert.ok(Date.now() - start >= 100)\n          t.assert.ok(Date.now() - start < 1000)\n          eventSourceInstance.close()\n          t.assert.ok(true)\n\n          done()\n        }\n      }\n\n      await once(eventSourceInstance, 'open')\n\n      clock.tick(10)\n      await once(eventSourceInstance, 'error')\n\n      clock.tick(100)\n    })\n  })\n\n  test('Should reconnect and send lastEventId', async (t) => {\n    t.plan(1)\n    const clock = FakeTimers.install()\n    after(() => clock.uninstall())\n\n    let requestCount = 0\n\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', { 'Content-Type': 'text/event-stream' })\n      res.write('id: 1337\\n\\n')\n      if (++requestCount === 2) {\n        t.assert.strictEqual(req.headers['last-event-id'], '1337')\n      }\n      res.end()\n    })\n    after(() => server.close())\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n\n    await once(eventSourceInstance, 'open')\n\n    clock.tick(10)\n    await once(eventSourceInstance, 'error')\n\n    clock.tick(defaultReconnectionTime)\n    await once(eventSourceInstance, 'open')\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-redirecting.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - redirecting', () => {\n  [301, 302, 307, 308].forEach((statusCode) => {\n    test(`Should redirect on ${statusCode} status code`, async (t) => {\n      const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        if (res.req.url === '/redirect') {\n          res.writeHead(statusCode, undefined, { Location: '/target' })\n          res.end()\n        } else if (res.req.url === '/target') {\n          res.writeHead(200, 'dummy', { 'Content-Type': 'text/event-stream' })\n          res.end()\n        }\n      })\n\n      await once(server.listen(0), 'listening')\n\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}/redirect`)\n      eventSourceInstance.onerror = (e) => {\n        t.assert.fail('Should not have errored')\n      }\n      eventSourceInstance.onopen = () => {\n        t.assert.strictEqual(eventSourceInstance.url, `http://localhost:${port}/redirect`)\n        eventSourceInstance.close()\n        server.close()\n      }\n    })\n  })\n\n  test('Stop trying to connect when getting a 204 response', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      if (res.req.url === '/redirect') {\n        res.writeHead(301, undefined, { Location: '/target' })\n        res.end()\n      } else if (res.req.url === '/target') {\n        res.writeHead(204, 'OK')\n        res.end()\n      }\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}/redirect`)\n    eventSourceInstance.onerror = (event) => {\n      t.assert.strictEqual(eventSourceInstance.url, `http://localhost:${port}/redirect`)\n      t.assert.strictEqual(eventSourceInstance.readyState, EventSource.CLOSED)\n      server.close()\n    }\n    eventSourceInstance.onopen = () => {\n      t.assert.fail('Should not have opened')\n    }\n  })\n\n  test('Throw when missing a Location header', async (t) => {\n    const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      if (res.req.url === '/redirect') {\n        res.writeHead(301, undefined)\n        res.end()\n      } else if (res.req.url === '/target') {\n        res.writeHead(204, 'OK')\n        res.end()\n      }\n    })\n\n    await once(server.listen(0), 'listening')\n    const port = server.address().port\n\n    const eventSourceInstance = new EventSource(`http://localhost:${port}/redirect`)\n    eventSourceInstance.onerror = () => {\n      t.assert.strictEqual(eventSourceInstance.url, `http://localhost:${port}/redirect`)\n      t.assert.strictEqual(eventSourceInstance.readyState, EventSource.CLOSED)\n      server.close()\n    }\n  })\n\n  test('Should set origin attribute of messages after redirecting', async (t) => {\n    const targetServer = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      if (res.req.url === '/target') {\n        res.writeHead(200, undefined, { 'Content-Type': 'text/event-stream' })\n        res.write('event: message\\ndata: test\\n\\n')\n      }\n    })\n\n    await once(targetServer.listen(0), 'listening')\n    const targetPort = targetServer.address().port\n\n    const sourceServer = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(301, undefined, { Location: `http://127.0.0.1:${targetPort}/target` })\n      res.end()\n    })\n\n    await once(sourceServer.listen(0), 'listening')\n    const sourcePort = sourceServer.address().port\n\n    const eventSourceInstance = new EventSource(`http://127.0.0.1:${sourcePort}/redirect`)\n    eventSourceInstance.onmessage = (event) => {\n      t.assert.strictEqual(event.origin, `http://127.0.0.1:${targetPort}`)\n      eventSourceInstance.close()\n      targetServer.close()\n      sourceServer.close()\n    }\n    eventSourceInstance.onerror = (e) => {\n      t.assert.fail('Should not have errored')\n    }\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-request-status-error.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { test, describe } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - status error', () => {\n  [204, 205, 210, 299, 404, 410, 503].forEach((statusCode) => {\n    test(`Should error on ${statusCode} status code`, async (t) => {\n      const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        res.writeHead(statusCode, 'dummy', { 'Content-Type': 'text/event-stream' })\n        res.end()\n      })\n\n      await once(server.listen(0), 'listening')\n      const port = server.address().port\n\n      const eventSourceInstance = new EventSource(`http://localhost:${port}`)\n      eventSourceInstance.onerror = (e) => {\n        t.assert.strictEqual(this.readyState, this.CLOSED)\n        eventSourceInstance.close()\n        server.close()\n      }\n      eventSourceInstance.onmessage = () => {\n        t.assert.fail('Should not have received a message')\n      }\n      eventSourceInstance.onopen = () => {\n        t.assert.fail('Should not have opened')\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-stream-bom.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { EventSourceStream } = require('../../lib/web/eventsource/eventsource-stream')\n\ndescribe('EventSourceStream - handle BOM', () => {\n  test('Remove BOM from the beginning of the stream. 1 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`\\uFEFF${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Remove BOM from the beginning of the stream. 2 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`\\uFEFF${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 2) {\n      stream.write(Buffer.from([content[i], content[i + 1]]))\n    }\n  })\n\n  test('Remove BOM from the beginning of the stream. 3 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`\\uFEFF${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 3) {\n      stream.write(Buffer.from([content[i], content[i + 1], content[i + 2]]))\n    }\n  })\n\n  test('Remove BOM from the beginning of the stream. 4 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`\\uFEFF${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 4) {\n      stream.write(Buffer.from([content[i], content[i + 1], content[i + 2], content[i + 3]]))\n    }\n  })\n\n  test('Not containing BOM from the beginning of the stream. 1 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 1) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Not containing BOM from the beginning of the stream. 2 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 2) {\n      stream.write(Buffer.from([content[i], content[i + 1]]))\n    }\n  })\n\n  test('Not containing BOM from the beginning of the stream. 3 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 3) {\n      stream.write(Buffer.from([content[i], content[i + 1], content[i + 2]]))\n    }\n  })\n\n  test('Not containing BOM from the beginning of the stream. 4 byte chunks', (t) => {\n    const dataField = 'data: Hello'\n    const content = Buffer.from(`${dataField}`, 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.parseLine = function (line) {\n      t.assert.strictEqual(line.byteLength, dataField.length)\n      t.assert.strictEqual(line.toString(), dataField)\n    }\n\n    for (let i = 0; i < content.length; i += 4) {\n      stream.write(Buffer.from([content[i], content[i + 1], content[i + 2], content[i + 3]]))\n    }\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-stream-parse-line.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { EventSourceStream } = require('../../lib/web/eventsource/eventsource-stream')\n\ndescribe('EventSourceStream - parseLine', () => {\n  const defaultEventSourceSettings = {\n    origin: 'example.com',\n    reconnectionTime: 1000\n  }\n\n  test('Should push an unmodified event when line is empty', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 0)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set the data field with empty string if not containing data', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('data:', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, '')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set the data field with empty string if not containing data (containing space after colon)', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('data: ', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, '')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set the data field with a string containing space if having more than one space after colon', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('data:   ', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, '  ')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set value properly, even if the line contains multiple colons', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('data: : ', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, ': ')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set the data field when containing data', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('data: Hello', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, 'Hello')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should ignore comments', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from(':comment', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 0)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set retry field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('retry: 1000', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, '1000')\n  })\n\n  test('Should set id field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('id: 1234', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, '1234')\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should set id field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('event: custom', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, 'custom')\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('Should ignore invalid field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n\n    stream.parseLine(Buffer.from('comment: invalid', 'utf8'), event)\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 0)\n    t.assert.strictEqual(event.data, undefined)\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('bogus retry', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n    'retry:3000\\nretry:1000x\\ndata:x'.split('\\n').forEach((line) => {\n      stream.parseLine(Buffer.from(line, 'utf8'), event)\n    })\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 2)\n    t.assert.strictEqual(event.data, 'x')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, '3000')\n  })\n\n  test('bogus id', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n    'id:3000\\nid:30\\x000\\ndata:x'.split('\\n').forEach((line) => {\n      stream.parseLine(Buffer.from(line, 'utf8'), event)\n    })\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 2)\n    t.assert.strictEqual(event.data, 'x')\n    t.assert.strictEqual(event.id, '3000')\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n\n  test('empty event', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    const event = {}\n    'event: \\ndata:data'.split('\\n').forEach((line) => {\n      stream.parseLine(Buffer.from(line, 'utf8'), event)\n    })\n\n    t.assert.strictEqual(typeof event, 'object')\n    t.assert.strictEqual(Object.keys(event).length, 1)\n    t.assert.strictEqual(event.data, 'data')\n    t.assert.strictEqual(event.id, undefined)\n    t.assert.strictEqual(event.event, undefined)\n    t.assert.strictEqual(event.retry, undefined)\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-stream-process-event.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { EventSourceStream } = require('../../lib/web/eventsource/eventsource-stream')\n\ndescribe('EventSourceStream - processEvent', () => {\n  const defaultEventSourceSettings = {\n    origin: 'example.com',\n    reconnectionTime: 1000\n  }\n\n  test('Should set the defined origin as the origin of the MessageEvent', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    stream.on('data', (event) => {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.type, 'message')\n      t.assert.strictEqual(event.options.data, null)\n      t.assert.strictEqual(event.options.lastEventId, undefined)\n      t.assert.strictEqual(event.options.origin, 'example.com')\n      t.assert.strictEqual(stream.state.reconnectionTime, 1000)\n    })\n\n    stream.on('error', (error) => {\n      t.assert.fail(error)\n    })\n\n    stream.processEvent({})\n  })\n\n  test('Should set reconnectionTime to 4000 if event contains retry field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    stream.processEvent({\n      retry: '4000'\n    })\n\n    t.assert.strictEqual(stream.state.reconnectionTime, 4000)\n  })\n\n  test('Dispatches a MessageEvent with data', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    stream.on('data', (event) => {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.type, 'message')\n      t.assert.strictEqual(event.options.data, 'Hello')\n      t.assert.strictEqual(event.options.lastEventId, undefined)\n      t.assert.strictEqual(event.options.origin, 'example.com')\n      t.assert.strictEqual(stream.state.reconnectionTime, 1000)\n    })\n\n    stream.on('error', (error) => {\n      t.assert.fail(error)\n    })\n\n    stream.processEvent({\n      data: 'Hello'\n    })\n  })\n\n  test('Dispatches a MessageEvent with lastEventId, when event contains id field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    stream.on('data', (event) => {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.type, 'message')\n      t.assert.strictEqual(event.options.data, null)\n      t.assert.strictEqual(event.options.lastEventId, '1234')\n      t.assert.strictEqual(event.options.origin, 'example.com')\n      t.assert.strictEqual(stream.state.reconnectionTime, 1000)\n    })\n\n    stream.processEvent({\n      id: '1234'\n    })\n  })\n\n  test('Dispatches a MessageEvent with lastEventId, reusing the persisted', (t) => {\n    // lastEventId\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings,\n        lastEventId: '1234'\n      }\n    })\n\n    stream.on('data', (event) => {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.type, 'message')\n      t.assert.strictEqual(event.options.data, null)\n      t.assert.strictEqual(event.options.lastEventId, '1234')\n      t.assert.strictEqual(event.options.origin, 'example.com')\n      t.assert.strictEqual(stream.state.reconnectionTime, 1000)\n    })\n\n    stream.processEvent({})\n  })\n\n  test('Dispatches a MessageEvent with type custom, when event contains type field', (t) => {\n    const stream = new EventSourceStream({\n      eventSourceSettings: {\n        ...defaultEventSourceSettings\n      }\n    })\n\n    stream.on('data', (event) => {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.type, 'custom')\n      t.assert.strictEqual(event.options.data, null)\n      t.assert.strictEqual(event.options.lastEventId, undefined)\n      t.assert.strictEqual(event.options.origin, 'example.com')\n      t.assert.strictEqual(stream.state.reconnectionTime, 1000)\n    })\n\n    stream.processEvent({\n      event: 'custom'\n    })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource-stream.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { EventSourceStream } = require('../../lib/web/eventsource/eventsource-stream')\n\ndescribe('EventSourceStream', () => {\n  test('ignore empty chunks', (t) => {\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.fail()\n    }\n    stream.write(Buffer.alloc(0))\n  })\n\n  test('Simple event with data field.', (t) => {\n    const content = Buffer.from('data: Hello\\n\\n', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'Hello')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should also process CR as EOL.', (t) => {\n    const content = Buffer.from('data: Hello\\r\\r', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'Hello')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should also process CRLF as EOL.', (t) => {\n    const content = Buffer.from('data: Hello\\r\\n\\r\\n', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'Hello')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should also process mixed CR and CRLF as EOL.', (t) => {\n    const content = Buffer.from('data: Hello\\r\\r\\n', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'Hello')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should also process mixed LF and CRLF as EOL.', (t) => {\n    const content = Buffer.from('data: Hello\\n\\r\\n', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'Hello')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should ignore comments', (t) => {\n    const content = Buffer.from(':data: Hello\\n\\n', 'utf8')\n\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, undefined)\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should fire two events.', (t) => {\n    // @see https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation\n    const content = Buffer.from('data\\n\\ndata\\ndata\\n\\ndata:', 'utf8')\n    const stream = new EventSourceStream()\n\n    let count = 0\n    stream.processEvent = function (event) {\n      switch (count) {\n        case 0: {\n          t.assert.strictEqual(typeof event, 'object')\n          t.assert.strictEqual(event.event, undefined)\n          t.assert.strictEqual(event.data, '')\n          t.assert.strictEqual(event.id, undefined)\n          t.assert.strictEqual(event.retry, undefined)\n          break\n        }\n        case 1: {\n          t.assert.strictEqual(typeof event, 'object')\n          t.assert.strictEqual(event.event, undefined)\n          t.assert.strictEqual(event.data, '\\n')\n          t.assert.strictEqual(event.id, undefined)\n          t.assert.strictEqual(event.retry, undefined)\n        }\n      }\n      count++\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('Should fire two identical events.', (t) => {\n    // @see https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation\n    const content = Buffer.from('data:test\\n\\ndata: test\\n\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, 'test')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    for (let i = 0; i < content.length; i++) {\n      stream.write(Buffer.from([content[i]]))\n    }\n  })\n\n  test('ignores empty comments', (t) => {\n    const content = Buffer.from('data: Hello\\n\\n:\\n\\ndata: World\\n\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    let count = 0\n\n    stream.processEvent = function (event) {\n      switch (count) {\n        case 0: {\n          t.assert.strictEqual(typeof event, 'object')\n          t.assert.strictEqual(event.event, undefined)\n          t.assert.strictEqual(event.data, 'Hello')\n          t.assert.strictEqual(event.id, undefined)\n          t.assert.strictEqual(event.retry, undefined)\n          break\n        }\n        case 1: {\n          t.assert.strictEqual(typeof event, 'object')\n          t.assert.strictEqual(event.event, undefined)\n          t.assert.strictEqual(event.data, 'World')\n          t.assert.strictEqual(event.id, undefined)\n          t.assert.strictEqual(event.retry, undefined)\n          break\n        }\n        default: {\n          t.assert.fail()\n        }\n      }\n      count++\n    }\n\n    stream.write(content)\n  })\n\n  test('comment fest', (t) => {\n    const longstring = new Array(2 * 1024 + 1).join('x')\n    const content = Buffer.from(`data:1\\r:\\0\\n:\\r\\ndata:2\\n:${longstring}\\rdata:3\\n:data:fail\\r:${longstring}\\ndata:4\\n\\n`, 'utf8')\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.data, '1\\n2\\n3\\n4')\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n    }\n\n    stream.write(content)\n  })\n\n  test('comment fest', (t) => {\n    const content = Buffer.from('data:\\n\\ndata\\ndata\\n\\ndata:test\\n\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    let count = 0\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n      switch (count) {\n        case 0: {\n          t.assert.strictEqual(event.data, '')\n          break\n        }\n        case 1: {\n          t.assert.strictEqual(event.data, '\\n')\n          break\n        }\n        case 2: {\n          t.assert.strictEqual(event.data, 'test')\n          break\n        }\n        default: {\n          t.assert.fail()\n        }\n      }\n      count++\n    }\n    stream.write(content)\n  })\n\n  test('newline test', (t) => {\n    const content = Buffer.from('data:test\\r\\ndata\\ndata:test\\r\\n\\r\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n      t.assert.strictEqual(event.data, 'test\\n\\ntest')\n    }\n    stream.write(content)\n  })\n\n  test('newline test', (t) => {\n    const content = Buffer.from('data:test\\n data\\ndata\\nfoobar:xxx\\njustsometext\\n:thisisacommentyay\\ndata:test\\n\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n      t.assert.strictEqual(event.data, 'test\\n\\ntest')\n    }\n    stream.write(content)\n  })\n\n  test('newline test', (t) => {\n    const content = Buffer.from('data:test\\n data\\ndata\\nfoobar:xxx\\njustsometext\\n:thisisacommentyay\\ndata:test\\n\\n', 'utf8')\n    const stream = new EventSourceStream()\n\n    stream.processEvent = function (event) {\n      t.assert.strictEqual(typeof event, 'object')\n      t.assert.strictEqual(event.event, undefined)\n      t.assert.strictEqual(event.id, undefined)\n      t.assert.strictEqual(event.retry, undefined)\n      t.assert.strictEqual(event.data, 'test\\n\\ntest')\n    }\n    stream.write(content)\n  })\n})\n"
  },
  {
    "path": "test/eventsource/eventsource.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { EventSource } = require('../../lib/web/eventsource/eventsource')\n\ndescribe('EventSource - constructor', () => {\n  test('Not providing url argument should throw', (t) => {\n    t.assert.throws(() => new EventSource(), TypeError)\n  })\n  test('Throw DOMException if URL is invalid', (t) => {\n    t.assert.throws(() => new EventSource('http:'), { message: /Invalid URL/ })\n  })\n})\n"
  },
  {
    "path": "test/eventsource/util.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { isASCIINumber, isValidLastEventId } = require('../../lib/web/eventsource/util')\n\ntest('isValidLastEventId', (t) => {\n  t.assert.strictEqual(isValidLastEventId('valid'), true)\n  t.assert.strictEqual(isValidLastEventId('in\\u0000valid'), false)\n  t.assert.strictEqual(isValidLastEventId('in\\x00valid'), false)\n  t.assert.strictEqual(isValidLastEventId('…'), true)\n})\n\ntest('isASCIINumber', (t) => {\n  t.assert.strictEqual(isASCIINumber('123'), true)\n  t.assert.strictEqual(isASCIINumber(''), false)\n  t.assert.strictEqual(isASCIINumber('123a'), false)\n})\n"
  },
  {
    "path": "test/examples.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { createServer } = require('node:http')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst examples = require('../docs/examples/request.js')\n\ntest('request examples', async (t) => {\n  t = tspl(t, { plan: 7 })\n\n  let lastReq\n  const exampleServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    lastReq = req\n    if (req.method === 'DELETE') {\n      res.statusCode = 204\n      return res.end()\n    } else if (req.method === 'POST') {\n      res.statusCode = 200\n      if (req.url === '/json') {\n        res.setHeader('content-type', 'application/json')\n        res.end('{\"hello\":\"JSON Response\"}')\n      } else {\n        res.end('hello=form')\n      }\n    } else {\n      res.statusCode = 200\n      res.end('hello')\n    }\n  })\n\n  const errorServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    lastReq = req\n    res.statusCode = 400\n    res.setHeader('content-type', 'application/json')\n    res.end('{\"error\":\"an error\"}')\n  })\n\n  after(() => exampleServer.close())\n  after(() => errorServer.close())\n\n  exampleServer.listen(0)\n  errorServer.listen(0)\n\n  await Promise.all([\n    once(exampleServer, 'listening'),\n    once(errorServer, 'listening')\n  ])\n\n  await examples.getRequest(exampleServer.address().port)\n  t.strictEqual(lastReq.method, 'GET')\n\n  await examples.postJSONRequest(exampleServer.address().port)\n  t.strictEqual(lastReq.method, 'POST')\n  t.strictEqual(lastReq.headers['content-type'], 'application/json')\n\n  await examples.postFormRequest(exampleServer.address().port)\n  t.strictEqual(lastReq.method, 'POST')\n  t.strictEqual(lastReq.headers['content-type'], 'application/x-www-form-urlencoded')\n\n  await examples.deleteRequest(exampleServer.address().port)\n  t.strictEqual(lastReq.method, 'DELETE')\n\n  await examples.deleteRequest(errorServer.address().port)\n  t.strictEqual(lastReq.method, 'DELETE')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/fetch/401-statuscode-no-infinite-loop.js",
    "content": "'use strict'\n\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { test } = require('node:test')\nconst assert = require('node:assert')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Receiving a 401 status code should not cause infinite retry loop', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 401\n    res.end('Unauthorized')\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const response = await fetch(`http://localhost:${server.address().port}`)\n  assert.strictEqual(response.status, 401)\n})\n"
  },
  {
    "path": "test/fetch/407-statuscode-window-null.js",
    "content": "'use strict'\n\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { test } = require('node:test')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Receiving a 407 status code w/ a window option present should reject', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 407\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  // if init.window exists, the spec tells us to set request.window to 'no-window',\n  // which later causes the request to be rejected if the status code is 407\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, { window: null }))\n})\n"
  },
  {
    "path": "test/fetch/abort.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('allows aborting with custom errors', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  await t.test('Using AbortSignal.timeout with cause', async (t) => {\n    t.plan(2)\n    try {\n      await fetch(`http://localhost:${server.address().port}`, {\n        signal: AbortSignal.timeout(50)\n      })\n      t.assert.fail('should throw')\n    } catch (err) {\n      if (err.name === 'TypeError') {\n        const cause = err.cause\n        t.assert.strictEqual(cause.name, 'HeadersTimeoutError')\n        t.assert.strictEqual(cause.code, 'UND_ERR_HEADERS_TIMEOUT')\n      } else if (err.name === 'TimeoutError') {\n        t.assert.strictEqual(err.code, DOMException.TIMEOUT_ERR)\n        t.assert.strictEqual(err.cause, undefined)\n      } else {\n        throw err\n      }\n    }\n  })\n\n  t.test('Error defaults to an AbortError DOMException', async () => {\n    const ac = new AbortController()\n    ac.abort() // no reason\n\n    await t.assert.rejects(\n      fetch(`http://localhost:${server.address().port}`, {\n        signal: ac.signal\n      }),\n      {\n        name: 'AbortError',\n        code: DOMException.ABORT_ERR\n      }\n    )\n  })\n})\n"
  },
  {
    "path": "test/fetch/abort2.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n/* global AbortController */\n\ntest('parallel fetch with the same AbortController works as expected', async (t) => {\n  const body = {\n    fixes: 1389,\n    bug: 'Ensure request is not aborted before enqueueing bytes into stream.'\n  }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    res.end(JSON.stringify(body))\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  const abortController = new AbortController()\n\n  async function makeRequest () {\n    const result = await fetch(`http://localhost:${server.address().port}`, {\n      signal: abortController.signal\n    }).then(response => response.json())\n\n    abortController.abort()\n    return result\n  }\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const requests = Array.from({ length: 10 }, makeRequest)\n  const result = await Promise.allSettled(requests)\n\n  // since the requests are running parallel, any of them could resolve first.\n  // therefore we cannot rely on the order of the requests sent.\n  const { resolved, rejected } = result.reduce((a, b) => {\n    if (b.status === 'rejected') {\n      a.rejected.push(b)\n    } else {\n      a.resolved.push(b)\n    }\n\n    return a\n  }, { resolved: [], rejected: [] })\n\n  t.assert.strictEqual(rejected.length, 9) // out of 10 requests, only 1 should succeed\n  t.assert.strictEqual(resolved.length, 1)\n\n  t.assert.ok(rejected.every(rej => rej.reason?.code === DOMException.ABORT_ERR))\n  t.assert.deepStrictEqual(resolved[0].value, body)\n})\n"
  },
  {
    "path": "test/fetch/about-uri.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\ntest('fetching about: uris', async (t) => {\n  await t.test('about:blank', async () => {\n    await t.assert.rejects(fetch('about:blank'))\n  })\n\n  await t.test('All other about: urls should return an error', async () => {\n    try {\n      await fetch('about:config')\n      t.assert.fail('fetching about:config should fail')\n    } catch (e) {\n      t.assert.ok(e, 'this error was expected')\n    }\n  })\n})\n"
  },
  {
    "path": "test/fetch/blob-uri.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\ntest('fetching blob: uris', async (t) => {\n  const blobContents = 'hello world'\n  /** @type {Blob} */\n  let blob\n  /** @type {string} */\n  let objectURL\n\n  t.beforeEach(() => {\n    blob = new Blob([blobContents])\n    objectURL = URL.createObjectURL(blob)\n  })\n\n  await t.test('a normal fetch request works', async () => {\n    const res = await fetch(objectURL)\n\n    t.assert.strictEqual(blobContents, await res.text())\n    t.assert.strictEqual(blob.type, res.headers.get('Content-Type'))\n    t.assert.strictEqual(`${blob.size}`, res.headers.get('Content-Length'))\n  })\n\n  await t.test('a range fetch request returns the inclusive byte range', async () => {\n    const res = await fetch(objectURL, {\n      headers: {\n        Range: 'bytes=1-3'\n      }\n    })\n\n    t.assert.strictEqual(res.status, 206)\n    t.assert.strictEqual(await res.text(), blobContents.slice(1, 4))\n    t.assert.strictEqual(res.headers.get('Content-Length'), '3')\n    t.assert.strictEqual(res.headers.get('Content-Range'), `bytes 1-3/${blob.size}`)\n  })\n\n  await t.test('non-GET method to blob: fails', async () => {\n    try {\n      await fetch(objectURL, {\n        method: 'POST'\n      })\n      t.assert.fail('expected POST to blob: uri to fail')\n    } catch (e) {\n      t.assert.ok(e, 'Got the expected error')\n    }\n  })\n\n  // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L36-L41\n  await t.test('fetching revoked URL should fail', async () => {\n    URL.revokeObjectURL(objectURL)\n\n    try {\n      await fetch(objectURL)\n      t.assert.fail('expected revoked blob: url to fail')\n    } catch (e) {\n      t.assert.ok(e, 'Got the expected error')\n    }\n  })\n\n  // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L28-L34\n  await t.test('works with a fragment', async () => {\n    const res = await fetch(objectURL + '#fragment')\n\n    t.assert.strictEqual(blobContents, await res.text())\n  })\n\n  // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56\n  await t.test('Appending a query string to blob: url should cause fetch to fail', async () => {\n    try {\n      await fetch(objectURL + '?querystring')\n      t.assert.fail('expected ?querystring blob: url to fail')\n    } catch (e) {\n      t.assert.ok(e, 'Got the expected error')\n    }\n  })\n\n  // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L58-L62\n  await t.test('Appending a path should cause fetch to fail', async () => {\n    try {\n      await fetch(objectURL + '/path')\n      t.assert.fail('expected /path blob: url to fail')\n    } catch (e) {\n      t.assert.ok(e, 'Got the expected error')\n    }\n  })\n\n  // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L64-L70\n  await t.test('these http methods should fail', async () => {\n    for (const method of ['HEAD', 'POST', 'DELETE', 'OPTIONS', 'PUT', 'CUSTOM']) {\n      try {\n        await fetch(objectURL, { method })\n        t.assert.fail(`${method} fetch should have failed`)\n      } catch (e) {\n        t.assert.ok(e, `${method} blob url - test succeeded`)\n      }\n    }\n  })\n})\n"
  },
  {
    "path": "test/fetch/bundle.js",
    "content": "'use strict'\r\n\r\nconst { test } = require('node:test')\r\n\r\nconst { Response, Request, FormData, Headers, MessageEvent, CloseEvent, ErrorEvent } = require('../../undici-fetch')\r\n\r\ntest('bundle sets constructor.name and .name properly', (t) => {\r\n  t.assert.strictEqual(new Response().constructor.name, 'Response')\r\n  t.assert.strictEqual(Response.name, 'Response')\r\n\r\n  t.assert.strictEqual(new Request('http://a').constructor.name, 'Request')\r\n  t.assert.strictEqual(Request.name, 'Request')\r\n\r\n  t.assert.strictEqual(new Headers().constructor.name, 'Headers')\r\n  t.assert.strictEqual(Headers.name, 'Headers')\r\n\r\n  t.assert.strictEqual(new FormData().constructor.name, 'FormData')\r\n  t.assert.strictEqual(FormData.name, 'FormData')\r\n})\r\n\r\ntest('regression test for https://github.com/nodejs/node/issues/50263', (t) => {\r\n  const request = new Request('https://a', {\r\n    headers: {\r\n      test: 'abc'\r\n    },\r\n    method: 'POST'\r\n  })\r\n\r\n  const request1 = new Request(request, { body: 'does not matter' })\r\n\r\n  t.assert.strictEqual(request1.headers.get('test'), 'abc')\r\n})\r\n\r\ntest('WebSocket related events are exported', (t) => {\r\n  t.assert.deepStrictEqual(typeof CloseEvent, 'function')\r\n  t.assert.deepStrictEqual(typeof MessageEvent, 'function')\r\n  t.assert.deepStrictEqual(typeof ErrorEvent, 'function')\r\n})\r\n"
  },
  {
    "path": "test/fetch/client-error-stack-trace.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { sep, basename, join } = require('node:path')\nconst { fetch, setGlobalDispatcher, getGlobalDispatcher, Agent } = require('../..')\n\nconst projectFolder = basename(join(__dirname, '..', '..'))\nconst { fetch: fetchIndex } = require('../../index-fetch')\n\nconst previousDispatcher = getGlobalDispatcher()\nsetGlobalDispatcher(new Agent({\n  headersTimeout: 500,\n  connectTimeout: 500\n}))\n\nafter(() => {\n  setGlobalDispatcher(previousDispatcher)\n})\n\ntest('FETCH: request errors and prints trimmed stack trace', async (t) => {\n  try {\n    await fetch('http://a.com')\n  } catch (error) {\n    const stackLines = error.stack.split('\\n')\n    t.assert.ok(stackLines[0].includes('TypeError: fetch failed'))\n    t.assert.ok(stackLines.some(line => line.includes(`lib${sep}web${sep}fetch${sep}index.js`)))\n    t.assert.ok(stackLines.some(line => line.includes(`${projectFolder}${sep}index.js`)))\n    t.assert.ok(stackLines.some(line => line.includes(__filename)))\n  }\n})\n\ntest('FETCH-index: request errors and prints trimmed stack trace', async (t) => {\n  try {\n    await fetchIndex('http://a.com')\n  } catch (error) {\n    const stackLines = error.stack.split('\\n')\n    t.assert.ok(stackLines[0].includes('TypeError: fetch failed'))\n    t.assert.ok(stackLines.some(line => line.includes(`lib${sep}web${sep}fetch${sep}index.js`)))\n    t.assert.ok(stackLines.some(line => line.includes(`${projectFolder}${sep}index-fetch.js`)))\n    t.assert.ok(stackLines.some(line => line.includes(__filename)))\n  }\n})\n"
  },
  {
    "path": "test/fetch/client-fetch.js",
    "content": "/* globals AbortController */\n\n'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { fetch, Response, Request, FormData } = require('../..')\nconst { Client, setGlobalDispatcher, getGlobalDispatcher, Agent } = require('../..')\nconst nodeFetch = require('../../index-fetch')\nconst { once } = require('node:events')\nconst { gzipSync } = require('node:zlib')\nconst { promisify } = require('node:util')\nconst { randomFillSync, createHash } = require('node:crypto')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst previousDispatcher = getGlobalDispatcher()\nsetGlobalDispatcher(new Agent({\n  keepAliveTimeout: 1,\n  keepAliveMaxTimeout: 1\n}))\n\nafter(() => {\n  setGlobalDispatcher(previousDispatcher)\n})\n\ntest('function signature', (t) => {\n  t.plan(2)\n\n  t.assert.strictEqual(fetch.name, 'fetch')\n  t.assert.strictEqual(fetch.length, 1)\n})\n\ntest('args validation', async (t) => {\n  t.plan(2)\n\n  await t.assert.rejects(fetch(), TypeError)\n  await t.assert.rejects(fetch('ftp://unsupported'), TypeError)\n})\n\ntest('request json', (t, done) => {\n  t.plan(1)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.deepStrictEqual(obj, await body.json())\n    done()\n  })\n})\n\ntest('request text', (t, done) => {\n  t.plan(1)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.strictEqual(JSON.stringify(obj), await body.text())\n    done()\n  })\n})\n\ntest('request arrayBuffer', (t, done) => {\n  t.plan(1)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.deepStrictEqual(Buffer.from(JSON.stringify(obj)), Buffer.from(await body.arrayBuffer()))\n    done()\n  })\n})\n\ntest('should set type of blob object to the value of the `Content-Type` header from response', (t, done) => {\n  t.plan(1)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const response = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.strictEqual('application/json', (await response.blob()).type)\n    done()\n  })\n})\n\ntest('pre aborted with readable request body', (t, done) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const ac = new AbortController()\n    ac.abort()\n    await fetch(`http://localhost:${server.address().port}`, {\n      signal: ac.signal,\n      method: 'POST',\n      body: new ReadableStream({\n        async cancel (reason) {\n          t.assert.strictEqual(reason.name, 'AbortError')\n        }\n      }),\n      duplex: 'half'\n    }).catch(err => {\n      t.assert.strictEqual(err.name, 'AbortError')\n    }).finally(done)\n  })\n})\n\ntest('pre aborted with closed readable request body', (t, done) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const ac = new AbortController()\n    ac.abort()\n    const body = new ReadableStream({\n      async start (c) {\n        t.assert.ok(true)\n        c.close()\n      },\n      async cancel (reason) {\n        t.assert.fail()\n      }\n    })\n    queueMicrotask(() => {\n      fetch(`http://localhost:${server.address().port}`, {\n        signal: ac.signal,\n        method: 'POST',\n        body,\n        duplex: 'half'\n      }).catch(err => {\n        t.assert.strictEqual(err.name, 'AbortError')\n      }).finally(done)\n    })\n  })\n})\n\ntest('unsupported formData 1', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'asdasdsad')\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`)\n      .then(res => res.formData())\n      .catch(err => {\n        t.assert.strictEqual(err.name, 'TypeError')\n      })\n      .finally(done)\n  })\n})\n\ntest('multipart formdata not base64', async (t) => {\n  t.plan(2)\n\n  // Construct example form data, with text and blob fields\n  const formData = new FormData()\n  formData.append('field1', 'value1')\n  const blob = new Blob(['example\\ntext file'], { type: 'text/plain' })\n  formData.append('field2', blob, 'file.txt')\n\n  const tempRes = new Response(formData)\n  const boundary = tempRes.headers.get('content-type').split('boundary=')[1]\n  const formRaw = await tempRes.text()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'multipart/form-data; boundary=' + boundary)\n    res.write(formRaw)\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  const listen = promisify(server.listen.bind(server))\n  await listen(0)\n\n  const res = await fetch(`http://localhost:${server.address().port}`)\n  const form = await res.formData()\n  t.assert.strictEqual(form.get('field1'), 'value1')\n\n  const text = await form.get('field2').text()\n  t.assert.strictEqual(text, 'example\\ntext file')\n})\n\ntest('multipart formdata base64', (t, done) => {\n  t.plan(1)\n\n  // Example form data with base64 encoding\n  const data = randomFillSync(Buffer.alloc(256))\n  const formRaw =\n    '------formdata-undici-0.5786922755719377\\r\\n' +\n    'Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\\r\\n' +\n    'Content-Type: application/octet-stream\\r\\n' +\n    'Content-Transfer-Encoding: base64\\r\\n' +\n    '\\r\\n' +\n    data.toString('base64') +\n    '\\r\\n' +\n    '------formdata-undici-0.5786922755719377--'\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    res.setHeader('content-type', 'multipart/form-data; boundary=----formdata-undici-0.5786922755719377')\n\n    for (let offset = 0; offset < formRaw.length;) {\n      res.write(formRaw.slice(offset, offset += 2))\n      await new Promise(resolve => setTimeout(resolve))\n    }\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`)\n      .then(res => res.formData())\n      .then(form => form.get('file').arrayBuffer())\n      .then(buffer => createHash('sha256').update(Buffer.from(buffer)).digest('base64'))\n      .then(digest => {\n        t.assert.strictEqual(createHash('sha256').update(data).digest('base64'), digest)\n      })\n      .finally(done)\n  })\n})\n\ntest('multipart fromdata non-ascii filed names', async (t) => {\n  t.plan(1)\n\n  const request = new Request('http://localhost', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'multipart/form-data; boundary=----formdata-undici-0.6204674738279623'\n    },\n    body:\n      '------formdata-undici-0.6204674738279623\\r\\n' +\n      'Content-Disposition: form-data; name=\"fiŝo\"\\r\\n' +\n      '\\r\\n' +\n      'value1\\r\\n' +\n      '------formdata-undici-0.6204674738279623--'\n  })\n\n  const form = await request.formData()\n  t.assert.strictEqual(form.get('fiŝo'), 'value1')\n})\n\ntest('busboy emit error', async (t) => {\n  t.plan(1)\n  const formData = new FormData()\n  formData.append('field1', 'value1')\n\n  const tempRes = new Response(formData)\n  const formRaw = await tempRes.text()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'multipart/form-data; boundary=wrongboundary')\n    res.write(formRaw)\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  const listen = promisify(server.listen.bind(server))\n  await listen(0)\n\n  const res = await fetch(`http://localhost:${server.address().port}`)\n  await t.assert.rejects(res.formData(), 'Unexpected end of multipart data')\n})\n\n// https://github.com/nodejs/undici/issues/2244\ntest('parsing formData preserve full path on files', async (t) => {\n  t.plan(1)\n  const formData = new FormData()\n  formData.append('field1', new File(['foo'], 'a/b/c/foo.txt'))\n\n  const tempRes = new Response(formData)\n  const form = await tempRes.formData()\n\n  t.assert.strictEqual(form.get('field1').name, 'a/b/c/foo.txt')\n})\n\ntest('urlencoded formData', (t, done) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'application/x-www-form-urlencoded')\n    res.end('field1=value1&field2=value2')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`)\n      .then(res => res.formData())\n      .then(formData => {\n        t.assert.strictEqual(formData.get('field1'), 'value1')\n        t.assert.strictEqual(formData.get('field2'), 'value2')\n      })\n      .finally(done)\n  })\n})\n\ntest('text with BOM', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'application/x-www-form-urlencoded')\n    res.end('\\uFEFFtest=\\uFEFF')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`)\n      .then(res => res.text())\n      .then(text => {\n        t.assert.strictEqual(text, 'test=\\uFEFF')\n      })\n      .finally(done)\n  })\n})\n\ntest('formData with BOM', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'application/x-www-form-urlencoded')\n    res.end('\\uFEFFtest=\\uFEFF')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`)\n      .then(res => res.formData())\n      .then(formData => {\n        t.assert.strictEqual(formData.get('\\uFEFFtest'), '\\uFEFF')\n      })\n      .finally(done)\n  })\n})\n\ntest('locked blob body', (t, done) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`)\n    const reader = res.body.getReader()\n    res.blob().catch(err => {\n      t.assert.strictEqual(err.message, 'Body is unusable: Body has already been read')\n      reader.cancel()\n    }).finally(done)\n  })\n})\n\ntest('disturbed blob body', (t, done) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`)\n    await res.blob().then(() => {\n      t.assert.ok(true)\n    })\n    await res.blob().catch(err => {\n      t.assert.strictEqual(err.message, 'Body is unusable: Body has already been read')\n    })\n    done()\n  })\n})\n\ntest('redirect with body', (t, done) => {\n  t.plan(3)\n\n  let count = 0\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let body = ''\n    for await (const chunk of req) {\n      body += chunk\n    }\n    t.assert.strictEqual(body, 'asd')\n    if (count++ === 0) {\n      res.setHeader('location', 'asd')\n      res.statusCode = 302\n      res.end()\n    } else {\n      res.end(String(count))\n    }\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'PUT',\n      body: 'asd'\n    })\n    t.assert.strictEqual(await res.text(), '2')\n    done()\n  })\n})\n\ntest('redirect with stream', (t, done) => {\n  t.plan(3)\n\n  const location = '/asd'\n  const body = 'hello!'\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    res.writeHead(302, { location })\n    let count = 0\n    const l = setInterval(() => {\n      res.write(body[count++])\n      if (count === body.length) {\n        res.end()\n        clearInterval(l)\n      }\n    }, 50)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`, {\n      redirect: 'manual'\n    })\n    t.assert.strictEqual(res.status, 302)\n    t.assert.strictEqual(res.headers.get('location'), location)\n    t.assert.strictEqual(await res.text(), body)\n    done()\n  })\n})\n\ntest('fail to extract locked body', (t) => {\n  t.plan(1)\n\n  const stream = new ReadableStream({})\n  const reader = stream.getReader()\n  try {\n    // eslint-disable-next-line\n    new Response(stream)\n  } catch (err) {\n    t.assert.strictEqual(err.name, 'TypeError')\n  }\n  reader.cancel()\n})\n\ntest('fail to extract locked body', (t) => {\n  t.plan(1)\n\n  const stream = new ReadableStream({})\n  const reader = stream.getReader()\n  try {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      method: 'PUT',\n      body: stream,\n      keepalive: true\n    })\n  } catch (err) {\n    t.assert.strictEqual(err.message, 'keepalive')\n  }\n  reader.cancel()\n})\n\ntest('post FormData with Blob', (t, done) => {\n  t.plan(1)\n\n  const body = new FormData()\n  body.append('field1', new Blob(['asd1']))\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'PUT',\n      body\n    })\n    t.assert.ok(/asd1/.test(await res.text()))\n    done()\n  })\n})\n\ntest('post FormData with File', (t, done) => {\n  t.plan(2)\n\n  const body = new FormData()\n  body.append('field1', new File(['asd1'], 'filename123'))\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const res = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'PUT',\n      body\n    })\n    const result = await res.text()\n    t.assert.ok(/asd1/.test(result))\n    t.assert.ok(/filename123/.test(result))\n    done()\n  })\n})\n\ntest('invalid url', async (t) => {\n  t.plan(1)\n\n  try {\n    await fetch('http://invalid')\n  } catch (e) {\n    t.assert.match(e.cause.message, /invalid/)\n  }\n})\n\ntest('custom agent', (t, done) => {\n  t.plan(2)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const dispatcher = new Client('http://localhost:' + server.address().port, {\n      keepAliveTimeout: 1,\n      keepAliveMaxTimeout: 1\n    })\n    const oldDispatch = dispatcher.dispatch\n    dispatcher.dispatch = function (options, handler) {\n      t.assert.ok(true)\n      return oldDispatch.call(this, options, handler)\n    }\n    const body = await fetch(`http://localhost:${server.address().port}`, {\n      dispatcher\n    })\n    t.assert.deepStrictEqual(obj, await body.json())\n    done()\n  })\n})\n\ntest('custom agent node fetch', (t, done) => {\n  t.plan(2)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify(obj))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const dispatcher = new Client('http://localhost:' + server.address().port, {\n      keepAliveTimeout: 1,\n      keepAliveMaxTimeout: 1\n    })\n    const oldDispatch = dispatcher.dispatch\n    dispatcher.dispatch = function (options, handler) {\n      t.assert.ok(true)\n      return oldDispatch.call(this, options, handler)\n    }\n    const body = await nodeFetch.fetch(`http://localhost:${server.address().port}`, {\n      dispatcher\n    })\n    t.assert.deepStrictEqual(obj, await body.json())\n    done()\n  })\n})\n\ntest('error on redirect', (t, done) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 302\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const errorCause = await fetch(`http://localhost:${server.address().port}`, {\n      redirect: 'error'\n    }).catch((e) => e.cause)\n\n    t.assert.strictEqual(errorCause.message, 'unexpected redirect')\n    done()\n  })\n})\n\n// https://github.com/nodejs/undici/issues/1527\ntest('fetching with Request object - issue #1527', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.ok(true)\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const body = JSON.stringify({ foo: 'bar' })\n  const request = new Request(`http://localhost:${server.address().port}`, {\n    method: 'POST',\n    body\n  })\n\n  await t.assert.doesNotReject(fetch(request))\n})\n\ntest('do not decode redirect body', (t, done) => {\n  t.plan(3)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/resource') {\n      t.assert.ok(true)\n      res.statusCode = 301\n      res.setHeader('location', '/resource/')\n      // Some dumb http servers set the content-encoding gzip\n      // even if there is no response\n      res.setHeader('content-encoding', 'gzip')\n      res.end()\n      return\n    }\n    t.assert.ok(true)\n    res.setHeader('content-encoding', 'gzip')\n    res.end(gzipSync(JSON.stringify(obj)))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}/resource`)\n    t.assert.strictEqual(JSON.stringify(obj), await body.text())\n    done()\n  })\n})\n\ntest('decode non-redirect body with location header', (t, done) => {\n  t.plan(2)\n\n  const obj = { asd: true }\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.ok(true)\n    res.statusCode = 201\n    res.setHeader('location', '/resource/')\n    res.setHeader('content-encoding', 'gzip')\n    res.end(gzipSync(JSON.stringify(obj)))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}/resource`)\n    t.assert.strictEqual(JSON.stringify(obj), await body.text())\n    done()\n  })\n})\n\ntest('Receiving non-Latin1 headers', async (t) => {\n  const ContentDisposition = [\n    'inline; filename=rock&roll.png',\n    'inline; filename=\"rock\\'n\\'roll.png\"',\n    'inline; filename=\"image â\\x80\\x94 copy (1).png\"; filename*=UTF-8\\'\\'image%20%E2%80%94%20copy%20(1).png',\n    'inline; filename=\"_å\\x9C\\x96ç\\x89\\x87_ð\\x9F\\x96¼_image_.png\"; filename*=UTF-8\\'\\'_%E5%9C%96%E7%89%87_%F0%9F%96%BC_image_.png',\n    'inline; filename=\"100 % loading&perf.png\"; filename*=UTF-8\\'\\'100%20%25%20loading%26perf.png'\n  ]\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    for (let i = 0; i < ContentDisposition.length; i++) {\n      res.setHeader(`Content-Disposition-${i + 1}`, ContentDisposition[i])\n    }\n\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const url = `http://localhost:${server.address().port}`\n  const response = await fetch(url, { method: 'HEAD' })\n  const cdHeaders = [...response.headers]\n    .filter(([k]) => k.startsWith('content-disposition'))\n    .map(([, v]) => v)\n  const lengths = cdHeaders.map(h => h.length)\n\n  t.assert.deepStrictEqual(cdHeaders, ContentDisposition)\n  t.assert.deepStrictEqual(lengths, [30, 34, 94, 104, 90])\n})\n"
  },
  {
    "path": "test/fetch/client-node-max-header-size.js",
    "content": "'use strict'\n\nconst { exec } = require('node:child_process')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test, describe, before, after } = require('node:test')\n\ndescribe('fetch respects --max-http-header-size', () => {\n  let server\n\n  before(async () => {\n    server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, 'OK', {\n        'Content-Length': 2\n      })\n      res.write('OK')\n      res.end()\n    }).listen(0)\n\n    await once(server, 'listening')\n  })\n\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  test(\"respect Node.js' --max-http-header-size\", (t, done) => {\n    t.plan(6)\n\n    const command = 'node -e \"require(\\'./undici-fetch.js\\').fetch(\\'http://localhost:' + server.address().port + '\\')\"'\n\n    exec(`${command} --max-http-header-size=1`, { stdio: 'pipe' }, (err, stdout, stderr) => {\n      t.assert.strictEqual(err.code, 1)\n      t.assert.strictEqual(stdout, '')\n      t.assert.match(stderr, /UND_ERR_HEADERS_OVERFLOW/, '--max-http-header-size=1 should throw')\n\n      exec(command, { stdio: 'pipe' }, (err, stdout, stderr) => {\n        t.assert.ifError(err)\n        t.assert.strictEqual(stdout, '')\n        t.assert.strictEqual(stderr, '', 'default max-http-header-size should not throw')\n\n        done()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/fetch/content-length.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { fetch, FormData } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// https://github.com/nodejs/undici/issues/1783\ntest('Content-Length is set when using a FormData body with fetch', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    // TODO: check the length's value once the boundary has a fixed length\n    t.assert.ok('content-length' in req.headers) // request has content-length header\n    t.assert.ok(!Number.isNaN(Number(req.headers['content-length'])))\n    res.end()\n  }).listen(0)\n\n  await once(server, 'listening')\n  t.after(closeServerAsPromise(server))\n\n  const fd = new FormData()\n  fd.set('file', new Blob(['hello world 👋'], { type: 'text/plain' }), 'readme.md')\n  fd.set('string', 'some string value')\n\n  await fetch(`http://localhost:${server.address().port}`, {\n    method: 'POST',\n    body: fd\n  })\n})\n"
  },
  {
    "path": "test/fetch/cookies.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test, describe, before, after } = require('node:test')\nconst { stringify: qsStringify } = require('node:querystring')\nconst { Client, fetch, Headers } = require('../..')\nconst pem = require('@metcoder95/https-pem')\nconst { createSecureServer } = require('node:http2')\n\ndescribe('cookies', () => {\n  let server\n\n  before(() => {\n    server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const searchParams = new URL(req.url, 'http://localhost').searchParams\n      if (searchParams.has('set-cookie')) {\n        res.setHeader('set-cookie', searchParams.get('set-cookie'))\n      }\n      res.end(req.headers.cookie)\n    })\n\n    return once(server.listen(0), 'listening')\n  })\n\n  after(() => {\n    server.close()\n    return once(server, 'close')\n  })\n\n  test('Can receive set-cookie headers from a server using fetch - issue #1262', async (t) => {\n    const query = qsStringify({\n      'set-cookie': 'name=value; Domain=example.com'\n    })\n    const response = await fetch(`http://localhost:${server.address().port}?${query}`)\n\n    t.assert.strictEqual(response.headers.get('set-cookie'), 'name=value; Domain=example.com')\n    t.assert.strictEqual(await response.text(), '')\n\n    const response2 = await fetch(`http://localhost:${server.address().port}?${query}`, {\n      credentials: 'include'\n    })\n\n    t.assert.strictEqual(response2.headers.get('set-cookie'), 'name=value; Domain=example.com')\n    t.assert.strictEqual(await response2.text(), '')\n  })\n\n  test('Can send cookies to a server with fetch - issue #1463', async (t) => {\n    const headersInit = [\n      new Headers([['cookie', 'value']]),\n      { cookie: 'value' },\n      [['cookie', 'value']]\n    ]\n\n    for (const headers of headersInit) {\n      const response = await fetch(`http://localhost:${server.address().port}`, { headers })\n      const text = await response.text()\n      t.assert.strictEqual(text, 'value')\n    }\n  })\n\n  test('Cookie header is delimited with a semicolon rather than a comma - issue #1905', async (t) => {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      headers: [\n        ['cookie', 'FOO=lorem-ipsum-dolor-sit-amet'],\n        ['cookie', 'BAR=the-quick-brown-fox']\n      ]\n    })\n\n    t.assert.strictEqual(await response.text(), 'FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox')\n  })\n\n  test('Can receive set-cookie headers from a http2 server using fetch - issue #2885', async (t) => {\n    const server = createSecureServer(pem)\n    server.on('stream', (stream, headers) => {\n      stream.respond({\n        'content-type': 'text/plain; charset=utf-8',\n        'x-method': headers[':method'],\n        'set-cookie': 'Space=Cat; Secure; HttpOnly',\n        ':status': 200\n      })\n\n      stream.end('test')\n    })\n\n    await once(server.listen(0), 'listening')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    const response = await fetch(\n      `https://localhost:${server.address().port}/`,\n      // Needs to be passed to disable the reject unauthorized\n      {\n        method: 'GET',\n        dispatcher: client,\n        headers: {\n          'content-type': 'text-plain'\n        }\n      }\n    )\n\n    t.assert.deepStrictEqual(response.headers.getSetCookie(), ['Space=Cat; Secure; HttpOnly'])\n    t.assert.strictEqual(await response.text(), 'test')\n\n    await client.close()\n    await new Promise((resolve, reject) => server.close(err => err ? reject(err) : resolve()))\n  })\n})\n"
  },
  {
    "path": "test/fetch/data-uri.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst {\n  URLSerializer,\n  stringPercentDecode,\n  parseMIMEType,\n  collectAnHTTPQuotedString\n} = require('../../lib/web/fetch/data-url')\nconst { fetch } = require('../..')\n\ntest('https://url.spec.whatwg.org/#concept-url-serializer', async (t) => {\n  await t.test('url scheme gets appended', (t) => {\n    const url = new URL('https://www.google.com/')\n    const serialized = URLSerializer(url)\n\n    t.assert.ok(serialized.startsWith(url.protocol))\n  })\n\n  await t.test('non-null url host with authentication', (t) => {\n    const url = new URL('https://username:password@google.com')\n    const serialized = URLSerializer(url)\n\n    t.assert.ok(serialized.includes(`//${url.username}:${url.password}`))\n    t.assert.ok(serialized.endsWith('@google.com/'))\n  })\n\n  await t.test('null url host', (t) => {\n    for (const url of ['web+demo:/.//not-a-host/', 'web+demo:/path/..//not-a-host/']) {\n      t.assert.strictEqual(\n        URLSerializer(new URL(url)),\n        'web+demo:/.//not-a-host/'\n      )\n    }\n  })\n\n  await t.test('url with query works', (t) => {\n    t.assert.strictEqual(\n      URLSerializer(new URL('https://www.google.com/?fetch=undici')),\n      'https://www.google.com/?fetch=undici'\n    )\n  })\n\n  await t.test('exclude fragment', (t) => {\n    t.assert.strictEqual(\n      URLSerializer(new URL('https://www.google.com/#frag')),\n      'https://www.google.com/#frag'\n    )\n\n    t.assert.strictEqual(\n      URLSerializer(new URL('https://www.google.com/#frag'), true),\n      'https://www.google.com/'\n    )\n  })\n})\n\ntest('https://url.spec.whatwg.org/#string-percent-decode', async (t) => {\n  await t.test('encodes %{2} in range properly', (t) => {\n    const input = '%FF'\n    const percentDecoded = stringPercentDecode(input)\n\n    t.assert.deepStrictEqual(percentDecoded, new Uint8Array([255]))\n  })\n\n  await t.test('encodes %{2} not in range properly', (t) => {\n    const input = 'Hello %XD World'\n    const percentDecoded = stringPercentDecode(input)\n    const expected = [...input].map(c => c.charCodeAt(0))\n\n    t.assert.deepStrictEqual(percentDecoded, new Uint8Array(expected))\n  })\n\n  await t.test('normal string works', (t) => {\n    const input = 'Hello world'\n    const percentDecoded = stringPercentDecode(input)\n    const expected = [...input].map(c => c.charCodeAt(0))\n\n    t.assert.deepStrictEqual(percentDecoded, Uint8Array.from(expected))\n  })\n})\n\ntest('https://mimesniff.spec.whatwg.org/#parse-a-mime-type', (t) => {\n  t.assert.deepStrictEqual(parseMIMEType('text/plain'), {\n    type: 'text',\n    subtype: 'plain',\n    parameters: new Map(),\n    essence: 'text/plain'\n  })\n\n  t.assert.deepStrictEqual(parseMIMEType('text/html;charset=\"shift_jis\"iso-2022-jp'), {\n    type: 'text',\n    subtype: 'html',\n    parameters: new Map([['charset', 'shift_jis']]),\n    essence: 'text/html'\n  })\n\n  t.assert.deepStrictEqual(parseMIMEType('application/javascript'), {\n    type: 'application',\n    subtype: 'javascript',\n    parameters: new Map(),\n    essence: 'application/javascript'\n  })\n})\n\ntest('https://fetch.spec.whatwg.org/#collect-an-http-quoted-string', async (t) => {\n  // https://fetch.spec.whatwg.org/#example-http-quoted-string\n  await t.test('first', (t) => {\n    const position = { position: 0 }\n\n    t.assert.strictEqual(collectAnHTTPQuotedString('\"\\\\', {\n      position: 0\n    }), '\"\\\\')\n    t.assert.strictEqual(collectAnHTTPQuotedString('\"\\\\', position, true), '\\\\')\n    t.assert.strictEqual(position.position, 2)\n  })\n\n  await t.test('second', (t) => {\n    const position = { position: 0 }\n    const input = '\"Hello\" World'\n\n    t.assert.strictEqual(collectAnHTTPQuotedString(input, {\n      position: 0\n    }), '\"Hello\"')\n    t.assert.strictEqual(collectAnHTTPQuotedString(input, position, true), 'Hello')\n    t.assert.strictEqual(position.position, 7)\n  })\n})\n\n// https://github.com/nodejs/undici/issues/1574\ntest('too long base64 url', async (t) => {\n  const inputStr = 'a'.repeat(1 << 20)\n  const base64 = Buffer.from(inputStr).toString('base64')\n  const dataURIPrefix = 'data:application/octet-stream;base64,'\n  const dataURL = dataURIPrefix + base64\n  try {\n    const res = await fetch(dataURL)\n    const buf = await res.arrayBuffer()\n    const outputStr = Buffer.from(buf).toString('ascii')\n    t.assert.strictEqual(outputStr, inputStr)\n  } catch (e) {\n    t.assert.fail(`failed to fetch ${dataURL}`)\n  }\n})\n\ntest('https://domain.com/#', (t) => {\n  t.plan(1)\n  const domain = 'https://domain.com/#a'\n  const serialized = URLSerializer(new URL(domain))\n  t.assert.strictEqual(serialized, domain)\n})\n\ntest('https://domain.com/?', (t) => {\n  t.plan(1)\n  const domain = 'https://domain.com/?a=b'\n  const serialized = URLSerializer(new URL(domain))\n  t.assert.strictEqual(serialized, domain)\n})\n\n// https://github.com/nodejs/undici/issues/2474\ntest('hash url', (t) => {\n  t.plan(1)\n  const domain = 'https://domain.com/#a#b'\n  const url = new URL(domain)\n  const serialized = URLSerializer(url, true)\n  t.assert.strictEqual(serialized, url.href.substring(0, url.href.length - url.hash.length))\n})\n\n// https://github.com/nodejs/undici/issues/2474\ntest('data url that includes the hash', async (t) => {\n  t.plan(1)\n  const dataURL = 'data:,node#js#'\n  try {\n    const res = await fetch(dataURL)\n    t.assert.strictEqual(await res.text(), 'node')\n  } catch (error) {\n    t.assert.fail(`failed to fetch ${dataURL}`)\n  }\n})\n"
  },
  {
    "path": "test/fetch/encoding.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test, before, after, describe } = require('node:test')\nconst { fetch, Client } = require('../..')\n\ndescribe('content-encoding handling', () => {\n  const gzipDeflateText = Buffer.from('H4sIAAAAAAAAA6uY89nj7MmT1wM5zuuf8gxkYZCfx5IFACQ8u/wVAAAA', 'base64')\n  const zstdText = Buffer.from('KLUv/QBYaQAASGVsbG8sIFdvcmxkIQ==', 'base64')\n\n  let server\n  before(async () => {\n    server = createServer({\n      noDelay: true\n    }, (req, res) => {\n      res.socket.setNoDelay(true)\n      if (\n        req.headers['accept-encoding'] === 'deflate, gzip' ||\n        req.headers['accept-encoding'] === 'DeFlAtE, GzIp'\n      ) {\n        res.writeHead(200,\n          {\n            'Content-Encoding': 'deflate, gzip',\n            'Content-Type': 'text/plain'\n          }\n        )\n        res.flushHeaders()\n        res.end(gzipDeflateText)\n      } else if (req.headers['accept-encoding'] === 'zstd') {\n        res.writeHead(200,\n          {\n            'Content-Encoding': 'zstd',\n            'Content-Type': 'text/plain'\n          }\n        )\n        res.flushHeaders()\n        res.end(zstdText)\n      } else {\n        res.writeHead(200,\n          {\n            'Content-Type': 'text/plain'\n          }\n        )\n        res.flushHeaders()\n        res.end('Hello, World!')\n      }\n    })\n    await once(server.listen(0), 'listening')\n  })\n\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  test('content-encoding header', async (t) => {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      keepalive: false,\n      headers: { 'accept-encoding': 'deflate, gzip' }\n    })\n\n    t.assert.strictEqual(response.headers.get('content-encoding'), 'deflate, gzip')\n    t.assert.strictEqual(response.headers.get('content-type'), 'text/plain')\n    t.assert.strictEqual(await response.text(), 'Hello, World!')\n  })\n\n  test('content-encoding header is case-iNsENsITIve', async (t) => {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      keepalive: false,\n      headers: { 'accept-encoding': 'DeFlAtE, GzIp' }\n    })\n\n    t.assert.strictEqual(response.headers.get('content-encoding'), 'deflate, gzip')\n    t.assert.strictEqual(response.headers.get('content-type'), 'text/plain')\n    t.assert.strictEqual(await response.text(), 'Hello, World!')\n  })\n\n  test('should decompress zstandard response',\n    { skip: typeof require('node:zlib').createZstdDecompress !== 'function' },\n    async (t) => {\n      const response = await fetch(`http://localhost:${server.address().port}`, {\n        keepalive: false,\n        headers: { 'accept-encoding': 'zstd' }\n      })\n\n      t.assert.strictEqual(response.headers.get('content-encoding'), 'zstd')\n      t.assert.strictEqual(response.headers.get('content-type'), 'text/plain')\n      t.assert.strictEqual(await response.text(), 'Hello, World!')\n    })\n})\n\ndescribe('content-encoding chain limit', () => {\n  // CVE fix: Limit the number of content-encodings to prevent resource exhaustion\n  // Similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206)\n  const MAX_CONTENT_ENCODINGS = 5\n\n  let server\n  before(async () => {\n    server = createServer({\n      noDelay: true\n    }, (req, res) => {\n      res.socket.setNoDelay(true)\n      const encodingCount = parseInt(req.headers['x-encoding-count'] || '1', 10)\n      const encodings = Array(encodingCount).fill('identity').join(', ')\n\n      res.writeHead(200, {\n        'Content-Encoding': encodings,\n        'Content-Type': 'text/plain'\n      })\n      res.flushHeaders()\n      res.end('test')\n    })\n    await once(server.listen(0, '127.0.0.1'), 'listening')\n  })\n\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  test(`should allow exactly ${MAX_CONTENT_ENCODINGS} content-encodings`, async (t) => {\n    const client = new Client(`http://127.0.0.1:${server.address().port}`)\n    t.after(() => client.close())\n\n    const response = await fetch(`http://127.0.0.1:${server.address().port}`, {\n      dispatcher: client,\n      keepalive: false,\n      headers: { 'x-encoding-count': String(MAX_CONTENT_ENCODINGS) }\n    })\n\n    t.assert.strictEqual(response.status, 200)\n    // identity encoding is a no-op, so the body should be passed through\n    t.assert.strictEqual(await response.text(), 'test')\n  })\n\n  test(`should reject more than ${MAX_CONTENT_ENCODINGS} content-encodings`, async (t) => {\n    const client = new Client(`http://127.0.0.1:${server.address().port}`)\n    t.after(() => client.close())\n\n    await t.assert.rejects(\n      fetch(`http://127.0.0.1:${server.address().port}`, {\n        dispatcher: client,\n        keepalive: false,\n        headers: { 'x-encoding-count': String(MAX_CONTENT_ENCODINGS + 1) }\n      }),\n      (err) => {\n        t.assert.ok(err.cause?.message.includes('content-encoding'))\n        return true\n      }\n    )\n  })\n\n  test('should reject excessive content-encoding chains', async (t) => {\n    const client = new Client(`http://127.0.0.1:${server.address().port}`)\n    t.after(() => client.close())\n\n    await t.assert.rejects(\n      fetch(`http://127.0.0.1:${server.address().port}`, {\n        dispatcher: client,\n        keepalive: false,\n        headers: { 'x-encoding-count': '100' }\n      }),\n      (err) => {\n        t.assert.ok(err.cause?.message.includes('content-encoding'))\n        return true\n      }\n    )\n  })\n})\n"
  },
  {
    "path": "test/fetch/exiting.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { closeServerAsPromise } = require('../utils/node-http')\nconst { once } = require('node:events')\nconst { createDeferredPromise } = require('../../lib/util/promise')\n\ntest('abort the request on the other side if the stream is canceled', async (t) => {\n  t.plan(1)\n\n  const promise = createDeferredPromise()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200)\n    res.write('hello')\n    req.on('aborted', () => {\n      t.assert.ok('aborted')\n      promise.resolve()\n    })\n    // Let's not end the response on purpose\n  })\n  t.after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n\n  const url = new URL(`http://127.0.0.1:${server.address().port}`)\n\n  const response = await fetch(url)\n\n  const reader = response.body.getReader()\n\n  try {\n    await reader.read()\n  } finally {\n    reader.releaseLock()\n    await response.body.cancel()\n  }\n\n  await promise.promise\n})\n"
  },
  {
    "path": "test/fetch/export-env-proxy-agent.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst undiciFetch = require('../../undici-fetch')\n\ntest('EnvHttpProxyAgent should be part of Node.js bundle', (t) => {\n  t.assert.strictEqual(typeof undiciFetch.EnvHttpProxyAgent, 'function')\n  t.assert.strictEqual(typeof undiciFetch.getGlobalDispatcher, 'function')\n  t.assert.strictEqual(typeof undiciFetch.setGlobalDispatcher, 'function')\n\n  const agent = new undiciFetch.EnvHttpProxyAgent()\n  const previousDispatcher = undiciFetch.getGlobalDispatcher()\n  undiciFetch.setGlobalDispatcher(agent)\n  t.after(() => {\n    undiciFetch.setGlobalDispatcher(previousDispatcher)\n  })\n  t.assert.strictEqual(undiciFetch.getGlobalDispatcher(), agent)\n})\n"
  },
  {
    "path": "test/fetch/fetch-leak.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst hasGC = typeof global.gc !== 'undefined'\n\ntest('do not leak', (t, done) => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n  t.plan(1)\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  let url\n  let isDone = false\n  server.listen(0, function attack () {\n    if (isDone) {\n      return\n    }\n    url ??= new URL(`http://127.0.0.1:${server.address().port}`)\n    const controller = new AbortController()\n    fetch(url, { signal: controller.signal })\n      .then(res => res.arrayBuffer())\n      .catch(() => {})\n      .then(attack)\n  })\n\n  let prev = Infinity\n  let count = 0\n  const interval = setInterval(() => {\n    isDone = true\n    global.gc()\n    const next = process.memoryUsage().heapUsed\n    if (next <= prev) {\n      t.assert.ok(true)\n      done()\n    } else if (count++ > 20) {\n      t.assert.fail()\n    } else {\n      prev = next\n    }\n  }, 1e3)\n  t.after(() => clearInterval(interval))\n})\n"
  },
  {
    "path": "test/fetch/fetch-timeouts.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\n\nconst { fetch, Agent } = require('../..')\nconst timers = require('../../lib/util/timers')\nconst { createServer } = require('node:http')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Fetch very long request, timeout overridden so no error', (t, done) => {\n  const minutes = 6\n  const msToDelay = 1000 * 60 * minutes\n\n  t.plan(1)\n\n  const clock = FakeTimers.install()\n  t.after(clock.uninstall.bind(clock))\n\n  const orgTimers = { ...timers }\n  Object.assign(timers, { setTimeout, clearTimeout })\n  t.after(() => {\n    Object.assign(timers, orgTimers)\n  })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, msToDelay)\n    clock.tick(msToDelay + 1)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    fetch(`http://localhost:${server.address().port}`, {\n      path: '/',\n      method: 'GET',\n      dispatcher: new Agent({\n        headersTimeout: 0,\n        connectTimeout: 0,\n        bodyTimeout: 0\n      })\n    })\n      .then((response) => response.text())\n      .then((response) => {\n        t.assert.strictEqual('hello', response)\n        done()\n      })\n      .catch((err) => {\n        // This should not happen, a timeout error should not occur\n        throw err\n      })\n\n    clock.tick(msToDelay - 1)\n  })\n})\n"
  },
  {
    "path": "test/fetch/fetch-url-after-redirect.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { fetch } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\nconst { promisify } = require('node:util')\n\ntest('after redirecting the url of the response is set to the target url', async (t) => {\n  // redirect-1 -> redirect-2 -> target\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    switch (res.req.url) {\n      case '/redirect-1':\n        res.writeHead(302, undefined, { Location: '/redirect-2' })\n        res.end()\n        break\n      case '/redirect-2':\n        res.writeHead(302, undefined, { Location: '/redirect-3' })\n        res.end()\n        break\n      case '/redirect-3':\n        res.writeHead(302, undefined, { Location: '/target' })\n        res.end()\n        break\n      case '/target':\n        res.writeHead(200, 'dummy', { 'Content-Type': 'text/plain' })\n        res.end()\n        break\n    }\n  })\n  t.after(closeServerAsPromise(server))\n\n  const listenAsync = promisify(server.listen.bind(server))\n  await listenAsync(0)\n  const { port } = server.address()\n  const response = await fetch(`http://127.0.0.1:${port}/redirect-1`)\n\n  t.assert.strictEqual(response.url, `http://127.0.0.1:${port}/target`)\n})\n\ntest('location header with non-ASCII character redirects to a properly encoded url', async (t) => {\n  // redirect -> %EC%95%88%EB%85%95 (안녕), not %C3%AC%C2%95%C2%88%C3%AB%C2%85%C2%95\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (res.req.url.endsWith('/redirect')) {\n      res.writeHead(302, undefined, { Location: `/${Buffer.from('안녕').toString('binary')}` })\n      res.end()\n    } else {\n      res.writeHead(200, 'dummy', { 'Content-Type': 'text/plain' })\n      res.end()\n    }\n  })\n  t.after(closeServerAsPromise(server))\n\n  const listenAsync = promisify(server.listen.bind(server))\n  await listenAsync(0)\n  const { port } = server.address()\n  const response = await fetch(`http://127.0.0.1:${port}/redirect`)\n\n  t.assert.strictEqual(response.url, `http://127.0.0.1:${port}/${encodeURIComponent('안녕')}`)\n})\n"
  },
  {
    "path": "test/fetch/fire-and-forget.js",
    "content": "'use strict'\n\nconst { randomFillSync } = require('node:crypto')\nconst { setTimeout: sleep, setImmediate: nextTick } = require('node:timers/promises')\nconst { test } = require('node:test')\nconst { fetch, Request, Response, Agent, setGlobalDispatcher, getGlobalDispatcher } = require('../..')\nconst { createServer } = require('node:http')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst blob = randomFillSync(new Uint8Array(1024 * 512))\n\nconst hasGC = typeof global.gc !== 'undefined'\n\n// https://github.com/nodejs/undici/issues/4150\ntest('test finalizer cloned request', async () => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n\n  const request = new Request('http://localhost', { method: 'POST', body: 'Hello' })\n\n  request.clone()\n\n  await nextTick()\n  // eslint-disable-next-line no-undef\n  gc()\n\n  await nextTick()\n  await request.arrayBuffer() // check consume body\n})\n\ntest('test finalizer cloned response', async () => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n\n  const response = new Response('Hello')\n\n  response.clone()\n\n  await nextTick()\n  // eslint-disable-next-line no-undef\n  gc()\n\n  await nextTick()\n  await response.arrayBuffer() // check consume body\n})\n\n// https://github.com/nodejs/undici/pull/4803\ntest('should not call cancel() during GC (new Response)', async () => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n\n  let response = new Response(new ReadableStream({\n    start () {},\n\n    pull (ctrl) {\n      ctrl.enqueue(new Uint8Array([72, 101, 108, 108, 111])) // Hello\n    },\n\n    cancel () {\n      throw new Error('should be unreachable')\n    }\n  }))\n\n  let cloned = response.clone()\n\n  const body = response.body\n\n  await nextTick()\n  cloned.body.cancel()\n\n  cloned = null\n  response = null\n\n  await nextTick()\n  // eslint-disable-next-line no-undef\n  gc()\n\n  await nextTick(); // handle 'uncaughtException' event\n  (function () {})(body) // save a reference without triggering linter warnings\n})\n\ntest('does not need the body to be consumed to continue', { timeout: 180_000 }, async (t) => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n  const agent = new Agent({\n    keepAliveMaxTimeout: 10,\n    keepAliveTimeoutThreshold: 10\n  })\n  const previousDispatcher = getGlobalDispatcher()\n  setGlobalDispatcher(agent)\n  t.after(() => {\n    setGlobalDispatcher(previousDispatcher)\n  })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200)\n    res.end(blob)\n  })\n  t.after(closeServerAsPromise(server))\n\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n\n  const url = new URL(`http://127.0.0.1:${server.address().port}`)\n\n  const batch = 50\n  const delay = 0\n  let total = 0\n  while (total < 5000) {\n    // eslint-disable-next-line no-undef\n    gc(true)\n    const array = new Array(batch)\n    for (let i = 0; i < batch; i += 2) {\n      array[i] = fetch(url).catch(() => {})\n      array[i + 1] = fetch(url).then(r => r.clone()).catch(() => {})\n    }\n    await Promise.all(array)\n    await sleep(delay)\n\n    console.log(\n      'RSS',\n      (process.memoryUsage.rss() / 1024 / 1024) | 0,\n      'MB after',\n      (total += batch) + ' fetch() requests'\n    )\n  }\n})\n"
  },
  {
    "path": "test/fetch/formdata-inspect-custom.js",
    "content": "'use strict'\n\nconst { FormData } = require('../../')\nconst { inspect } = require('node:util')\nconst { test } = require('node:test')\n\ntest('FormData class custom inspection', (t) => {\n  const formData = new FormData()\n  formData.append('username', 'john_doe')\n  formData.append('email', 'john@example.com')\n\n  const expectedOutput = \"FormData {\\n  username: 'john_doe',\\n  email: 'john@example.com'\\n}\"\n\n  t.assert.deepStrictEqual(inspect(formData), expectedOutput)\n})\n"
  },
  {
    "path": "test/fetch/formdata.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { FormData, Response, Request } = require('../../')\nconst { isFormDataLike } = require('../../lib/core/util')\n\ntest('arg validation', (t) => {\n  const form = new FormData()\n\n  // constructor\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new FormData('asd')\n  }, TypeError)\n\n  // append\n  t.assert.throws(() => {\n    FormData.prototype.append.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.append()\n  }, TypeError)\n  t.assert.throws(() => {\n    form.append('k', 'not usv', '')\n  }, TypeError)\n\n  // delete\n  t.assert.throws(() => {\n    FormData.prototype.delete.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.delete()\n  }, TypeError)\n\n  // get\n  t.assert.throws(() => {\n    FormData.prototype.get.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.get()\n  }, TypeError)\n\n  // getAll\n  t.assert.throws(() => {\n    FormData.prototype.getAll.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.getAll()\n  }, TypeError)\n\n  // has\n  t.assert.throws(() => {\n    FormData.prototype.has.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.has()\n  }, TypeError)\n\n  // set\n  t.assert.throws(() => {\n    FormData.prototype.set.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    form.set('k')\n  }, TypeError)\n  t.assert.throws(() => {\n    form.set('k', 'not usv', '')\n  }, TypeError)\n\n  // iterator\n  t.assert.throws(() => {\n    Reflect.apply(FormData.prototype[Symbol.iterator], null)\n  }, TypeError)\n\n  // toStringTag\n  t.assert.doesNotThrow(() => {\n    FormData.prototype[Symbol.toStringTag].charAt(0)\n  })\n})\n\ntest('set blob', (t) => {\n  const form = new FormData()\n\n  form.set('key', new Blob([]), undefined)\n  t.assert.strictEqual(form.get('key').name, 'blob')\n\n  form.set('key1', new Blob([]), null)\n  t.assert.strictEqual(form.get('key1').name, 'null')\n})\n\ntest('append file', (t) => {\n  const form = new FormData()\n  form.set('asd', new File([], 'asd1', { type: 'text/plain' }), 'asd2')\n  form.append('asd2', new File([], 'asd1'), 'asd2')\n\n  t.assert.strictEqual(form.has('asd'), true)\n  t.assert.strictEqual(form.has('asd2'), true)\n  t.assert.strictEqual(form.get('asd').name, 'asd2')\n  t.assert.strictEqual(form.get('asd2').name, 'asd2')\n  t.assert.strictEqual(form.get('asd').type, 'text/plain')\n  form.delete('asd')\n  t.assert.strictEqual(form.get('asd'), null)\n  t.assert.strictEqual(form.has('asd2'), true)\n  t.assert.strictEqual(form.has('asd'), false)\n})\n\ntest('append blob', async (t) => {\n  const form = new FormData()\n  form.set('asd', new Blob(['asd1'], { type: 'text/plain' }))\n\n  t.assert.strictEqual(form.has('asd'), true)\n  t.assert.strictEqual(form.get('asd').type, 'text/plain')\n  t.assert.strictEqual(await form.get('asd').text(), 'asd1')\n  form.delete('asd')\n  t.assert.strictEqual(form.get('asd'), null)\n\n  form.append('key', new Blob([]), undefined)\n  t.assert.strictEqual(form.get('key').name, 'blob')\n\n  form.append('key1', new Blob([]), null)\n  t.assert.strictEqual(form.get('key1').name, 'null')\n})\n\ntest('append string', (t) => {\n  const form = new FormData()\n  form.set('k1', 'v1')\n  form.set('k2', 'v2')\n  t.assert.deepStrictEqual([...form], [['k1', 'v1'], ['k2', 'v2']])\n  t.assert.strictEqual(form.has('k1'), true)\n  t.assert.strictEqual(form.get('k1'), 'v1')\n  form.append('k1', 'v1+')\n  t.assert.deepStrictEqual(form.getAll('k1'), ['v1', 'v1+'])\n  form.set('k2', 'v1++')\n  t.assert.strictEqual(form.get('k2'), 'v1++')\n  form.delete('asd')\n  t.assert.strictEqual(form.get('asd'), null)\n})\n\ntest('formData.entries', async (t) => {\n  const form = new FormData()\n\n  await t.test('with 0 entries', (t) => {\n    t.plan(1)\n\n    const entries = [...form.entries()]\n    t.assert.deepStrictEqual(entries, [])\n  })\n\n  await t.test('with 1+ entries', (t) => {\n    t.plan(2)\n\n    form.set('k1', 'v1')\n    form.set('k2', 'v2')\n\n    const entries = [...form.entries()]\n    const entries2 = [...form.entries()]\n    t.assert.deepStrictEqual(entries, [['k1', 'v1'], ['k2', 'v2']])\n    t.assert.deepStrictEqual(entries, entries2)\n  })\n})\n\ntest('formData.keys', async (t) => {\n  const form = new FormData()\n\n  await t.test('with 0 keys', (t) => {\n    t.plan(1)\n\n    const keys = [...form.entries()]\n    t.assert.deepStrictEqual(keys, [])\n  })\n\n  await t.test('with 1+ keys', (t) => {\n    t.plan(2)\n\n    form.set('k1', 'v1')\n    form.set('k2', 'v2')\n\n    const keys = [...form.keys()]\n    const keys2 = [...form.keys()]\n    t.assert.deepStrictEqual(keys, ['k1', 'k2'])\n    t.assert.deepStrictEqual(keys, keys2)\n  })\n})\n\ntest('formData.values', async (t) => {\n  const form = new FormData()\n\n  await t.test('with 0 values', (t) => {\n    t.plan(1)\n\n    const values = [...form.values()]\n    t.assert.deepStrictEqual(values, [])\n  })\n\n  await t.test('with 1+ values', (t) => {\n    t.plan(2)\n\n    form.set('k1', 'v1')\n    form.set('k2', 'v2')\n\n    const values = [...form.values()]\n    const values2 = [...form.values()]\n    t.assert.deepStrictEqual(values, ['v1', 'v2'])\n    t.assert.deepStrictEqual(values, values2)\n  })\n})\n\ntest('formData forEach', async (t) => {\n  await t.test('invalid arguments', (t) => {\n    t.assert.throws(() => {\n      FormData.prototype.forEach.call({})\n    }, TypeError('Illegal invocation'))\n\n    t.assert.throws(() => {\n      const fd = new FormData()\n\n      fd.forEach({})\n    }, TypeError)\n  })\n\n  await t.test('with a callback', (t) => {\n    const fd = new FormData()\n\n    fd.set('a', 'b')\n    fd.set('c', 'd')\n\n    let i = 0\n    fd.forEach((value, key, self) => {\n      if (i++ === 0) {\n        t.assert.strictEqual(value, 'b')\n        t.assert.strictEqual(key, 'a')\n      } else {\n        t.assert.strictEqual(value, 'd')\n        t.assert.strictEqual(key, 'c')\n      }\n\n      t.assert.strictEqual(fd, self)\n    })\n  })\n\n  await t.test('with a thisArg', (t) => {\n    const fd = new FormData()\n    fd.set('b', 'a')\n\n    fd.forEach(function (value, key, self) {\n      t.assert.strictEqual(this, globalThis)\n      t.assert.strictEqual(fd, self)\n      t.assert.strictEqual(key, 'b')\n      t.assert.strictEqual(value, 'a')\n    })\n\n    const thisArg = Symbol('thisArg')\n    fd.forEach(function () {\n      t.assert.strictEqual(this, thisArg)\n    }, thisArg)\n  })\n})\n\ntest('formData toStringTag', (t) => {\n  const form = new FormData()\n  t.assert.strictEqual(form[Symbol.toStringTag], 'FormData')\n  t.assert.strictEqual(FormData.prototype[Symbol.toStringTag], 'FormData')\n})\n\ntest('formData.constructor.name', (t) => {\n  const form = new FormData()\n  t.assert.strictEqual(form.constructor.name, 'FormData')\n})\n\ntest('formData should be an instance of FormData', async (t) => {\n  await t.test('Invalid class FormData', (t) => {\n    class FormData {\n      constructor () {\n        this.data = []\n      }\n\n      append (key, value) {\n        this.data.push([key, value])\n      }\n\n      get (key) {\n        return this.data.find(([k]) => k === key)\n      }\n    }\n\n    const form = new FormData()\n    t.assert.strictEqual(isFormDataLike(form), false)\n  })\n\n  await t.test('Invalid function FormData', (t) => {\n    function FormData () {\n      const data = []\n      return {\n        append (key, value) {\n          data.push([key, value])\n        },\n        get (key) {\n          return data.find(([k]) => k === key)\n        }\n      }\n    }\n\n    const form = new FormData()\n    t.assert.strictEqual(isFormDataLike(form), false)\n  })\n\n  await t.test('Valid FormData', (t) => {\n    const form = new FormData()\n    t.assert.strictEqual(isFormDataLike(form), true)\n  })\n})\n\ntest('FormData should be compatible with third-party libraries', (t) => {\n  t.plan(1)\n\n  class FormData {\n    constructor () {\n      this.data = []\n    }\n\n    get [Symbol.toStringTag] () {\n      return 'FormData'\n    }\n\n    append () {}\n    delete () {}\n    get () {}\n    getAll () {}\n    has () {}\n    set () {}\n    entries () {}\n    keys () {}\n    values () {}\n    forEach () {}\n  }\n\n  const form = new FormData()\n  t.assert.strictEqual(isFormDataLike(form), true)\n})\n\ntest('arguments', (t) => {\n  t.assert.strictEqual(FormData.length, 0)\n  t.assert.strictEqual(FormData.prototype.append.length, 2)\n  t.assert.strictEqual(FormData.prototype.delete.length, 1)\n  t.assert.strictEqual(FormData.prototype.get.length, 1)\n  t.assert.strictEqual(FormData.prototype.getAll.length, 1)\n  t.assert.strictEqual(FormData.prototype.has.length, 1)\n  t.assert.strictEqual(FormData.prototype.set.length, 2)\n})\n\n// https://github.com/nodejs/undici/pull/1814\ntest('FormData returned from bodyMixin.formData is not a clone', async (t) => {\n  const fd = new FormData()\n  fd.set('foo', 'bar')\n\n  const res = new Response(fd)\n  fd.set('foo', 'foo')\n\n  const fd2 = await res.formData()\n\n  t.assert.strictEqual(fd2.get('foo'), 'bar')\n  t.assert.strictEqual(fd.get('foo'), 'foo')\n\n  fd2.set('foo', 'baz')\n\n  t.assert.strictEqual(fd2.get('foo'), 'baz')\n  t.assert.strictEqual(fd.get('foo'), 'foo')\n})\n\ntest('.formData() with multipart/form-data body that ends with --\\r\\n', async (t) => {\n  const request = new Request('http://localhost', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'multipart/form-data; boundary=----formdata-undici-0.6204674738279623'\n    },\n    body:\n      '------formdata-undici-0.6204674738279623\\r\\n' +\n      'Content-Disposition: form-data; name=\"fiŝo\"\\r\\n' +\n      '\\r\\n' +\n      'value1\\r\\n' +\n      '------formdata-undici-0.6204674738279623--\\r\\n'\n  })\n\n  await request.formData()\n})\n"
  },
  {
    "path": "test/fetch/general.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst {\n  FormData,\n  Headers,\n  Request,\n  Response\n} = require('../../index')\n\ntest('Symbol.toStringTag descriptor', (t) => {\n  for (const cls of [\n    FormData,\n    Headers,\n    Request,\n    Response\n  ]) {\n    const desc = Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag)\n    t.assert.deepStrictEqual(desc, {\n      value: cls.name,\n      writable: false,\n      enumerable: false,\n      configurable: true\n    })\n  }\n})\n"
  },
  {
    "path": "test/fetch/headers-case.js",
    "content": "'use strict'\n\nconst { fetch, Headers, Request } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { test } = require('node:test')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Headers retain keys case-sensitive', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.ok(req.rawHeaders.includes('Content-Type'))\n\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const url = `http://localhost:${server.address().port}`\n  for (const headers of [\n    new Headers([['Content-Type', 'text/plain']]),\n    { 'Content-Type': 'text/plain' },\n    [['Content-Type', 'text/plain']]\n  ]) {\n    await fetch(url, { headers })\n  }\n  // see https://github.com/nodejs/undici/pull/3183\n  await fetch(new Request(url, { headers: [['Content-Type', 'text/plain']] }), { method: 'GET' })\n})\n"
  },
  {
    "path": "test/fetch/headers-inspect-custom.js",
    "content": "'use strict'\n\nconst { Headers } = require('../../lib/web/fetch/headers')\nconst { test } = require('node:test')\nconst util = require('node:util')\n\ntest('Headers class custom inspection', (t) => {\n  const headers = new Headers()\n  headers.set('Content-Type', 'application/json')\n  headers.set('Authorization', 'Bearer token')\n\n  const inspectedOutput = util.inspect(headers, { depth: 1 })\n\n  const expectedOutput = \"Headers { 'Content-Type': 'application/json', Authorization: 'Bearer token' }\"\n  t.assert.strictEqual(inspectedOutput, expectedOutput)\n})\n"
  },
  {
    "path": "test/fetch/headers.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Headers, fill, setHeadersGuard } = require('../../lib/web/fetch/headers')\nconst { once } = require('node:events')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Headers initialization', async (t) => {\n  await t.test('allows undefined', (t) => {\n    t.plan(1)\n\n    t.assert.doesNotThrow(() => new Headers())\n  })\n\n  await t.test('with array of header entries', async (t) => {\n    await t.test('fails on invalid array-based init', (t) => {\n      t.plan(3)\n      t.assert.throws(\n        () => new Headers([['undici', 'fetch'], ['fetch']]),\n        TypeError('Headers constructor: expected name/value pair to be length 2, found 1.')\n      )\n      t.assert.throws(() => new Headers(['undici', 'fetch', 'fetch']), TypeError)\n      t.assert.throws(\n        () => new Headers([0, 1, 2]),\n        TypeError('Headers constructor: init[0] (0) is not iterable.')\n      )\n    })\n\n    await t.test('allows even length init', (t) => {\n      t.plan(1)\n      const init = [['undici', 'fetch'], ['fetch', 'undici']]\n      t.assert.doesNotThrow(() => new Headers(init))\n    })\n\n    await t.test('fails for event flattened init', (t) => {\n      t.plan(1)\n      const init = ['undici', 'fetch', 'fetch', 'undici']\n      t.assert.throws(\n        () => new Headers(init),\n        TypeError('Headers constructor: init[0] (\"undici\") is not iterable.')\n      )\n    })\n  })\n\n  await t.test('with object of header entries', (t) => {\n    t.plan(1)\n    const init = {\n      undici: 'fetch',\n      fetch: 'undici'\n    }\n    t.assert.doesNotThrow(() => new Headers(init))\n  })\n\n  await t.test('fails silently if a boxed primitive object is passed', (t) => {\n    t.plan(3)\n    /* eslint-disable no-new-wrappers */\n    t.assert.doesNotThrow(() => new Headers(new Number()))\n    t.assert.doesNotThrow(() => new Headers(new Boolean()))\n    t.assert.doesNotThrow(() => new Headers(new String()))\n    /* eslint-enable no-new-wrappers */\n  })\n\n  await t.test('fails if primitive is passed', (t) => {\n    t.plan(2)\n    const expectedTypeError = TypeError\n    t.assert.throws(() => new Headers(1), expectedTypeError)\n    t.assert.throws(() => new Headers('1'), expectedTypeError)\n  })\n\n  await t.test('allows some weird stuff (because of webidl)', (t) => {\n    t.assert.doesNotThrow(() => {\n      new Headers(function () {}) // eslint-disable-line no-new\n    })\n\n    t.assert.doesNotThrow(() => {\n      new Headers(Function) // eslint-disable-line no-new\n    })\n  })\n\n  await t.test('allows a myriad of header values to be passed', (t) => {\n    t.plan(4)\n\n    // Headers constructor uses Headers.append\n\n    t.assert.doesNotThrow(() => new Headers([\n      ['a', ['b', 'c']],\n      ['d', ['e', 'f']]\n    ]), 'allows any array values')\n    t.assert.doesNotThrow(() => new Headers([\n      ['key', null]\n    ]), 'allows null values')\n    t.assert.throws(() => new Headers([\n      ['key']\n    ]), 'throws when 2 arguments are not passed')\n    t.assert.throws(() => new Headers([\n      ['key', 'value', 'value2']\n    ]), 'throws when too many arguments are passed')\n  })\n\n  await t.test('accepts headers as objects with array values', (t) => {\n    t.plan(1)\n    const headers = new Headers({\n      c: '5',\n      b: ['3', '4'],\n      a: ['1', '2']\n    })\n\n    t.assert.deepStrictEqual([...headers.entries()], [\n      ['a', '1,2'],\n      ['b', '3,4'],\n      ['c', '5']\n    ])\n  })\n})\n\ntest('Headers append', async (t) => {\n  await t.test('adds valid header entry to instance', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    const name = 'undici'\n    const value = 'fetch'\n    t.assert.doesNotThrow(() => headers.append(name, value))\n    t.assert.strictEqual(headers.get(name), value)\n  })\n\n  await t.test('adds valid header to existing entry', (t) => {\n    t.plan(4)\n    const headers = new Headers()\n\n    const name = 'undici'\n    const value1 = 'fetch1'\n    const value2 = 'fetch2'\n    const value3 = 'fetch3'\n    headers.append(name, value1)\n    t.assert.strictEqual(headers.get(name), value1)\n    t.assert.doesNotThrow(() => headers.append(name, value2))\n    t.assert.doesNotThrow(() => headers.append(name, value3))\n    t.assert.strictEqual(headers.get(name), [value1, value2, value3].join(', '))\n  })\n\n  await t.test('throws on invalid entry', (t) => {\n    t.plan(3)\n    const headers = new Headers()\n\n    t.assert.throws(() => headers.append(), 'throws on missing name and value')\n    t.assert.throws(() => headers.append('undici'), 'throws on missing value')\n    t.assert.throws(() => headers.append('invalid @ header ? name', 'valid value'), 'throws on invalid name')\n  })\n})\n\ntest('Headers delete', async (t) => {\n  await t.test('deletes valid header entry from instance', (t) => {\n    t.plan(3)\n    const headers = new Headers()\n\n    const name = 'undici'\n    const value = 'fetch'\n    headers.append(name, value)\n    t.assert.strictEqual(headers.get(name), value)\n    t.assert.doesNotThrow(() => headers.delete(name))\n    t.assert.strictEqual(headers.get(name), null)\n  })\n\n  await t.test('does not mutate internal list when no match is found', (t) => {\n    t.plan(3)\n\n    const headers = new Headers()\n    const name = 'undici'\n    const value = 'fetch'\n    headers.append(name, value)\n    t.assert.strictEqual(headers.get(name), value)\n    t.assert.doesNotThrow(() => headers.delete('not-undici'))\n    t.assert.strictEqual(headers.get(name), value)\n  })\n\n  await t.test('throws on invalid entry', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    t.assert.throws(() => headers.delete(), 'throws on missing namee')\n    t.assert.throws(() => headers.delete('invalid @ header ? name'), 'throws on invalid name')\n  })\n\n  // https://github.com/nodejs/undici/issues/2429\n  await t.test('`Headers#delete` returns undefined', (t) => {\n    t.plan(2)\n    const headers = new Headers({ test: 'test' })\n\n    t.assert.strictEqual(headers.delete('test'), undefined)\n    t.assert.strictEqual(headers.delete('test2'), undefined)\n  })\n})\n\ntest('Headers get', async (t) => {\n  await t.test('returns null if not found in instance', (t) => {\n    t.plan(1)\n    const headers = new Headers()\n    headers.append('undici', 'fetch')\n\n    t.assert.strictEqual(headers.get('not-undici'), null)\n  })\n\n  await t.test('returns header values from valid header name', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    const name = 'undici'; const value1 = 'fetch1'; const value2 = 'fetch2'\n    headers.append(name, value1)\n    t.assert.strictEqual(headers.get(name), value1)\n    headers.append(name, value2)\n    t.assert.strictEqual(headers.get(name), [value1, value2].join(', '))\n  })\n\n  await t.test('throws on invalid entry', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    t.assert.throws(() => headers.get(), 'throws on missing name')\n    t.assert.throws(() => headers.get('invalid @ header ? name'), 'throws on invalid name')\n  })\n})\n\ntest('Headers has', async (t) => {\n  await t.test('returns boolean existence for a header name', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    const name = 'undici'\n    headers.append('not-undici', 'fetch')\n    t.assert.strictEqual(headers.has(name), false)\n    headers.append(name, 'fetch')\n    t.assert.strictEqual(headers.has(name), true)\n  })\n\n  await t.test('throws on invalid entry', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    t.assert.throws(() => headers.has(), 'throws on missing name')\n    t.assert.throws(() => headers.has('invalid @ header ? name'), 'throws on invalid name')\n  })\n})\n\ntest('Headers set', async (t) => {\n  await t.test('sets valid header entry to instance', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    const name = 'undici'\n    const value = 'fetch'\n    headers.append('not-undici', 'fetch')\n    t.assert.doesNotThrow(() => headers.set(name, value))\n    t.assert.strictEqual(headers.get(name), value)\n  })\n\n  await t.test('overwrites existing entry', (t) => {\n    t.plan(4)\n    const headers = new Headers()\n\n    const name = 'undici'\n    const value1 = 'fetch1'\n    const value2 = 'fetch2'\n    t.assert.doesNotThrow(() => headers.set(name, value1))\n    t.assert.strictEqual(headers.get(name), value1)\n    t.assert.doesNotThrow(() => headers.set(name, value2))\n    t.assert.strictEqual(headers.get(name), value2)\n  })\n\n  await t.test('allows setting a myriad of values', (t) => {\n    t.plan(4)\n    const headers = new Headers()\n\n    t.assert.doesNotThrow(() => headers.set('a', ['b', 'c']), 'sets array values properly')\n    t.assert.doesNotThrow(() => headers.set('b', null), 'allows setting null values')\n    t.assert.throws(() => headers.set('c'), 'throws when 2 arguments are not passed')\n    t.assert.doesNotThrow(() => headers.set('c', 'd', 'e'), 'ignores extra arguments')\n  })\n\n  await t.test('throws on invalid entry', (t) => {\n    t.plan(3)\n    const headers = new Headers()\n\n    t.assert.throws(() => headers.set(), 'throws on missing name and value')\n    t.assert.throws(() => headers.set('undici'), 'throws on missing value')\n    t.assert.throws(() => headers.set('invalid @ header ? name', 'valid value'), 'throws on invalid name')\n  })\n\n  // https://github.com/nodejs/undici/issues/2431\n  await t.test('`Headers#set` returns undefined', (t) => {\n    t.plan(2)\n    const headers = new Headers()\n\n    t.assert.strictEqual(headers.set('a', 'b'), undefined)\n\n    t.assert.ok(!(headers.set('c', 'd') instanceof Map))\n  })\n})\n\ntest('Headers forEach', async (t) => {\n  const headers = new Headers([['a', 'b'], ['c', 'd']])\n\n  await t.test('standard', (t) => {\n    t.assert.strictEqual(typeof headers.forEach, 'function')\n\n    headers.forEach((value, key, headerInstance) => {\n      t.assert.ok(value === 'b' || value === 'd')\n      t.assert.ok(key === 'a' || key === 'c')\n      t.assert.strictEqual(headers, headerInstance)\n    })\n  })\n\n  await t.test('when no thisArg is set, it is globalThis', (t) => {\n    headers.forEach(function () {\n      t.assert.strictEqual(this, globalThis)\n    })\n  })\n\n  await t.test('with thisArg', (t) => {\n    const thisArg = { a: Math.random() }\n    headers.forEach(function () {\n      t.assert.strictEqual(this, thisArg)\n    }, thisArg)\n  })\n})\n\ntest('Headers as Iterable', async (t) => {\n  await t.test('should freeze values while iterating', (t) => {\n    t.plan(1)\n    const init = [\n      ['foo', '123'],\n      ['bar', '456']\n    ]\n    const expected = [\n      ['foo', '123'],\n      ['x-x-bar', '456']\n    ]\n    const headers = new Headers(init)\n    for (const [key, val] of headers) {\n      headers.delete(key)\n      headers.set(`x-${key}`, val)\n    }\n    t.assert.deepStrictEqual([...headers], expected)\n  })\n\n  await t.test('returns combined and sorted entries using .forEach()', (t) => {\n    t.plan(8)\n    const init = [\n      ['a', '1'],\n      ['b', '2'],\n      ['c', '3'],\n      ['abc', '4'],\n      ['b', '5']\n    ]\n    const expected = [\n      ['a', '1'],\n      ['abc', '4'],\n      ['b', '2, 5'],\n      ['c', '3']\n    ]\n    const headers = new Headers(init)\n    const that = {}\n    let i = 0\n    headers.forEach(function (value, key, _headers) {\n      t.assert.deepStrictEqual(expected[i++], [key, value])\n      t.assert.strictEqual(this, that)\n    }, that)\n  })\n\n  await t.test('returns combined and sorted entries using .entries()', (t) => {\n    t.plan(4)\n    const init = [\n      ['a', '1'],\n      ['b', '2'],\n      ['c', '3'],\n      ['abc', '4'],\n      ['b', '5']\n    ]\n    const expected = [\n      ['a', '1'],\n      ['abc', '4'],\n      ['b', '2, 5'],\n      ['c', '3']\n    ]\n    const headers = new Headers(init)\n    let i = 0\n    for (const header of headers.entries()) {\n      t.assert.deepStrictEqual(header, expected[i++])\n    }\n  })\n\n  await t.test('returns combined and sorted keys using .keys()', (t) => {\n    t.plan(4)\n    const init = [\n      ['a', '1'],\n      ['b', '2'],\n      ['c', '3'],\n      ['abc', '4'],\n      ['b', '5']\n    ]\n    const expected = ['a', 'abc', 'b', 'c']\n    const headers = new Headers(init)\n    let i = 0\n    for (const key of headers.keys()) {\n      t.assert.strictEqual(key, expected[i++])\n    }\n  })\n\n  await t.test('returns combined and sorted values using .values()', (t) => {\n    t.plan(4)\n    const init = [\n      ['a', '1'],\n      ['b', '2'],\n      ['c', '3'],\n      ['abc', '4'],\n      ['b', '5']\n    ]\n    const expected = ['1', '4', '2, 5', '3']\n    const headers = new Headers(init)\n    let i = 0\n    for (const value of headers.values()) {\n      t.assert.strictEqual(value, expected[i++])\n    }\n  })\n\n  await t.test('returns combined and sorted entries using for...of loop', (t) => {\n    t.plan(5)\n    const init = [\n      ['a', '1'],\n      ['b', '2'],\n      ['c', '3'],\n      ['abc', '4'],\n      ['b', '5'],\n      ['d', ['6', '7']]\n    ]\n    const expected = [\n      ['a', '1'],\n      ['abc', '4'],\n      ['b', '2, 5'],\n      ['c', '3'],\n      ['d', '6,7']\n    ]\n    let i = 0\n    for (const header of new Headers(init)) {\n      t.assert.deepStrictEqual(header, expected[i++])\n    }\n  })\n\n  await t.test('validate append ordering', (t) => {\n    t.plan(1)\n    const headers = new Headers([['b', '2'], ['c', '3'], ['e', '5']])\n    headers.append('d', '4')\n    headers.append('a', '1')\n    headers.append('f', '6')\n    headers.append('c', '7')\n    headers.append('abc', '8')\n\n    const expected = [...new Map([\n      ['a', '1'],\n      ['abc', '8'],\n      ['b', '2'],\n      ['c', '3, 7'],\n      ['d', '4'],\n      ['e', '5'],\n      ['f', '6']\n    ])]\n\n    t.assert.deepStrictEqual([...headers], expected)\n  })\n\n  await t.test('always use the same prototype Iterator', (t) => {\n    const HeadersIteratorNext = Function.call.bind(new Headers()[Symbol.iterator]().next)\n\n    const init = [\n      ['a', '1'],\n      ['b', '2']\n    ]\n\n    const headers = new Headers(init)\n    const iterator = headers[Symbol.iterator]()\n    t.assert.deepStrictEqual(HeadersIteratorNext(iterator), { value: init[0], done: false })\n    t.assert.deepStrictEqual(HeadersIteratorNext(iterator), { value: init[1], done: false })\n    t.assert.deepStrictEqual(HeadersIteratorNext(iterator), { value: undefined, done: true })\n  })\n})\n\ntest('arg validation', (t) => {\n  // fill\n  t.assert.throws(() => {\n    fill({}, 0)\n  }, TypeError)\n\n  const headers = new Headers()\n\n  // constructor\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Headers(0)\n  }, TypeError)\n\n  // get [Symbol.toStringTag]\n  t.assert.doesNotThrow(() => {\n    Object.prototype.toString.call(Headers.prototype)\n  })\n\n  // toString\n  t.assert.doesNotThrow(() => {\n    Headers.prototype.toString.call(null)\n  })\n\n  // append\n  t.assert.throws(() => {\n    Headers.prototype.append.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.append()\n  }, TypeError)\n\n  // delete\n  t.assert.throws(() => {\n    Headers.prototype.delete.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.delete()\n  }, TypeError)\n\n  // get\n  t.assert.throws(() => {\n    Headers.prototype.get.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.get()\n  }, TypeError)\n\n  // has\n  t.assert.throws(() => {\n    Headers.prototype.has.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.has()\n  }, TypeError)\n\n  // set\n  t.assert.throws(() => {\n    Headers.prototype.set.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.set()\n  }, TypeError)\n\n  // forEach\n  t.assert.throws(() => {\n    Headers.prototype.forEach.call(null)\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.forEach()\n  }, TypeError)\n  t.assert.throws(() => {\n    headers.forEach(1)\n  }, TypeError)\n\n  // inspect\n  t.assert.throws(() => {\n    Headers.prototype[Symbol.for('nodejs.util.inspect.custom')].call(null)\n  }, TypeError)\n})\n\ntest('function signature verification', async (t) => {\n  await t.test('function length', (t) => {\n    t.assert.strictEqual(Headers.prototype.append.length, 2)\n    t.assert.strictEqual(Headers.prototype.constructor.length, 0)\n    t.assert.strictEqual(Headers.prototype.delete.length, 1)\n    t.assert.strictEqual(Headers.prototype.entries.length, 0)\n    t.assert.strictEqual(Headers.prototype.forEach.length, 1)\n    t.assert.strictEqual(Headers.prototype.get.length, 1)\n    t.assert.strictEqual(Headers.prototype.has.length, 1)\n    t.assert.strictEqual(Headers.prototype.keys.length, 0)\n    t.assert.strictEqual(Headers.prototype.set.length, 2)\n    t.assert.strictEqual(Headers.prototype.values.length, 0)\n    t.assert.strictEqual(Headers.prototype[Symbol.iterator].length, 0)\n    t.assert.strictEqual(Headers.prototype.toString.length, 0)\n  })\n\n  await t.test('function equality', (t) => {\n    t.assert.strictEqual(Headers.prototype.entries, Headers.prototype[Symbol.iterator])\n    t.assert.strictEqual(Headers.prototype.toString, Object.prototype.toString)\n  })\n\n  await t.test('toString and Symbol.toStringTag', (t) => {\n    t.assert.strictEqual(Object.prototype.toString.call(Headers.prototype), '[object Headers]')\n    t.assert.strictEqual(Headers.prototype[Symbol.toStringTag], 'Headers')\n    t.assert.strictEqual(Headers.prototype.toString.call(null), '[object Null]')\n  })\n})\n\ntest('various init paths of Headers', (t) => {\n  const h1 = new Headers()\n  const h2 = new Headers({})\n  const h3 = new Headers(undefined)\n  t.assert.strictEqual([...h1.entries()].length, 0)\n  t.assert.strictEqual([...h2.entries()].length, 0)\n  t.assert.strictEqual([...h3.entries()].length, 0)\n})\n\ntest('immutable guard', (t) => {\n  const headers = new Headers()\n  headers.set('key', 'val')\n  setHeadersGuard(headers, 'immutable')\n\n  t.assert.throws(() => {\n    headers.set('asd', 'asd')\n  })\n  t.assert.throws(() => {\n    headers.append('asd', 'asd')\n  })\n  t.assert.throws(() => {\n    headers.delete('asd')\n  })\n  t.assert.strictEqual(headers.get('key'), 'val')\n  t.assert.strictEqual(headers.has('key'), true)\n})\n\ntest('request-no-cors guard', (t) => {\n  const headers = new Headers()\n  setHeadersGuard(headers, 'request-no-cors')\n  t.assert.doesNotThrow(() => { headers.set('key', 'val') })\n  t.assert.doesNotThrow(() => { headers.append('key', 'val') })\n})\n\ntest('invalid headers', (t) => {\n  t.assert.doesNotThrow(() => new Headers({ \"abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-.^_`|~\": 'test' }))\n\n  const chars = '\"(),/:;<=>?@[\\\\]{}'.split('')\n\n  for (const char of chars) {\n    t.assert.throws(() => new Headers({ [char]: 'test' }), TypeError, `The string \"${char}\" should throw an error.`)\n  }\n\n  for (const byte of ['\\r', '\\n', '\\t', ' ', String.fromCharCode(128), '']) {\n    t.assert.throws(() => {\n      new Headers().set(byte, 'test')\n    }, TypeError, 'invalid header name')\n  }\n\n  for (const byte of [\n    '\\0',\n    '\\r',\n    '\\n'\n  ]) {\n    t.assert.throws(() => {\n      new Headers().set('a', `a${byte}b`)\n    }, TypeError, 'not allowed at all in header value')\n  }\n\n  t.assert.doesNotThrow(() => {\n    new Headers().set('a', '\\r')\n  })\n\n  t.assert.doesNotThrow(() => {\n    new Headers().set('a', '\\n')\n  })\n\n  t.assert.throws(() => {\n    new Headers().set('a', Symbol('symbol'))\n  }, TypeError, 'symbols should throw')\n})\n\ntest('headers that might cause a ReDoS', (t) => {\n  t.assert.doesNotThrow(() => {\n    // This test will time out if the ReDoS attack is successful.\n    const headers = new Headers()\n    const attack = 'a' + '\\t'.repeat(500_000) + '\\ta'\n    headers.append('fhqwhgads', attack)\n  })\n})\n\ntest('Headers.prototype.getSetCookie', async (t) => {\n  await t.test('Mutating the returned list does not affect the set-cookie list', (t) => {\n    const h = new Headers([\n      ['set-cookie', 'a=b'],\n      ['set-cookie', 'c=d']\n    ])\n\n    const old = h.getSetCookie()\n    h.getSetCookie().push('oh=no')\n    const now = h.getSetCookie()\n\n    t.assert.deepStrictEqual(old, now)\n  })\n\n  // https://github.com/nodejs/undici/issues/1935\n  await t.test('When Headers are cloned, so are the cookies (single entry)', async (t) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('Set-Cookie', 'test=onetwo')\n      res.end('Hello World!')\n    }).listen(0)\n\n    await once(server, 'listening')\n    t.after(closeServerAsPromise(server))\n\n    const res = await fetch(`http://localhost:${server.address().port}`)\n    const entries = Object.fromEntries(res.headers.entries())\n\n    t.assert.deepStrictEqual(res.headers.getSetCookie(), ['test=onetwo'])\n    t.assert.ok('set-cookie' in entries)\n  })\n\n  await t.test('When Headers are cloned, so are the cookies (multiple entries)', async (t) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('Set-Cookie', ['test=onetwo', 'test=onetwothree'])\n      res.end('Hello World!')\n    }).listen(0)\n\n    await once(server, 'listening')\n    t.after(closeServerAsPromise(server))\n\n    const res = await fetch(`http://localhost:${server.address().port}`)\n    const entries = Object.fromEntries(res.headers.entries())\n\n    t.assert.deepStrictEqual(res.headers.getSetCookie(), ['test=onetwo', 'test=onetwothree'])\n    t.assert.ok('set-cookie' in entries)\n  })\n\n  await t.test('When Headers are cloned, so are the cookies (Headers constructor)', (t) => {\n    const headers = new Headers([['set-cookie', 'a'], ['set-cookie', 'b']])\n\n    t.assert.deepStrictEqual([...headers], [...new Headers(headers)])\n  })\n})\n\ntest('When the value is updated, update the cache', (t) => {\n  t.plan(2)\n  const expected = [['a', 'a'], ['b', 'b'], ['c', 'c']]\n  const headers = new Headers(expected)\n  t.assert.deepStrictEqual([...headers], expected)\n  headers.append('d', 'd')\n  t.assert.deepStrictEqual([...headers], [...expected, ['d', 'd']])\n})\n\ntest('Symbol.iterator is only accessed once', (t) => {\n  t.plan(1)\n\n  const dict = new Proxy({}, {\n    get () {\n      t.assert.ok(true)\n\n      return function * () {}\n    }\n  })\n\n  new Headers(dict) // eslint-disable-line no-new\n})\n\ntest('Invalid Symbol.iterators', (t) => {\n  t.plan(3)\n\n  t.assert.throws(() => new Headers({ [Symbol.iterator]: null }), TypeError)\n  t.assert.throws(() => new Headers({ [Symbol.iterator]: undefined }), TypeError)\n  t.assert.throws(() => {\n    const obj = { [Symbol.iterator]: null }\n    Object.defineProperty(obj, Symbol.iterator, { enumerable: false })\n\n    new Headers(obj) // eslint-disable-line no-new\n  }, TypeError)\n})\n\n// https://github.com/nodejs/undici/issues/3829\ntest('Invalid key/value records passed to constructor (issue #3829)', (t) => {\n  t.assert.throws(\n    () => new Headers({ [Symbol('x-fake-header')]: '??' }),\n    new TypeError('Headers constructor: Key Symbol(x-fake-header) in init is a symbol, which cannot be converted to a ByteString.')\n  )\n\n  t.assert.throws(\n    () => new Headers({ 'x-fake-header': Symbol('why is this here?') }),\n    new TypeError('Headers constructor: init[\"x-fake-header\"] is a symbol, which cannot be converted to a ByteString.')\n  )\n})\n"
  },
  {
    "path": "test/fetch/headerslist-sortedarray.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { HeadersList, compareHeaderName } = require('../../lib/web/fetch/headers')\n\nconst characters = 'abcdefghijklmnopqrstuvwxyz0123456789'\nconst charactersLength = characters.length\n\nfunction generateAsciiString (length) {\n  let result = ''\n  for (let i = 0; i < length; ++i) {\n    result += characters[Math.floor(Math.random() * charactersLength)]\n  }\n  return result\n}\n\nconst SORT_RUN = 4000\n\ntest('toSortedArray (fast-path)', (t) => {\n  for (let i = 0; i < SORT_RUN; ++i) {\n    const headersList = new HeadersList()\n    for (let j = 0; j < 32; ++j) {\n      headersList.append(generateAsciiString(4), generateAsciiString(4))\n    }\n    t.assert.deepStrictEqual(headersList.toSortedArray(), [...headersList].sort(compareHeaderName))\n  }\n})\n\ntest('toSortedArray (slow-path)', (t) => {\n  for (let i = 0; i < SORT_RUN; ++i) {\n    const headersList = new HeadersList()\n    for (let j = 0; j < 64; ++j) {\n      headersList.append(generateAsciiString(4), generateAsciiString(4))\n    }\n    t.assert.deepStrictEqual(headersList.toSortedArray(), [...headersList].sort(compareHeaderName))\n  }\n})\n"
  },
  {
    "path": "test/fetch/http2.js",
    "content": "'use strict'\n\nconst { createSecureServer } = require('node:http2')\nconst { createServer } = require('node:http')\nconst { createReadStream, readFileSync } = require('node:fs')\nconst { once } = require('node:events')\nconst { Readable } = require('node:stream')\n\nconst { test } = require('node:test')\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client, fetch, Headers } = require('../..')\n\nconst { closeClientAndServerAsPromise } = require('../utils/node-http')\n\ntest('[Fetch] Issue#2311', async (t) => {\n  const expectedBody = 'hello from client!'\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }), async (req, res) => {\n    let body = ''\n\n    req.setEncoding('utf8')\n\n    res.writeHead(200, {\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': req.headers['x-my-header']\n    })\n\n    for await (const chunk of req) {\n      body += chunk\n    }\n\n    res.end(body)\n  })\n\n  t.plan(2)\n\n  server.listen()\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'POST',\n      dispatcher: client,\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text-plain'\n      },\n      body: expectedBody\n    }\n  )\n\n  const responseBody = await response.text()\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  t.assert.strictEqual(responseBody, expectedBody)\n  t.assert.strictEqual(response.headers.get('x-custom-h2'), 'foo')\n})\n\ntest('[Fetch] Simple GET with h2', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedRequestBody = 'hello h2!'\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      'x-method': headers[':method'],\n      ':status': 200\n    })\n\n    stream.end(expectedRequestBody)\n  })\n\n  t.plan(5)\n\n  server.listen()\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'GET',\n      dispatcher: client,\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text-plain'\n      }\n    }\n  )\n\n  const responseBody = await response.text()\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  t.assert.strictEqual(responseBody, expectedRequestBody)\n  t.assert.strictEqual(response.headers.get('x-method'), 'GET')\n  t.assert.strictEqual(response.headers.get('x-custom-h2'), 'foo')\n  // https://github.com/nodejs/undici/issues/2415\n  t.assert.throws(() => {\n    response.headers.get(':status')\n  }, TypeError)\n\n  // See https://fetch.spec.whatwg.org/#concept-response-status-message\n  t.assert.strictEqual(response.statusText, '')\n})\n\ntest('[Fetch] Should handle h2 request with body (string or buffer)', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'hello from client!'\n  const expectedRequestBody = 'hello h2!'\n  const requestBody = []\n\n  server.on('stream', async (stream, headers) => {\n    stream.on('data', chunk => requestBody.push(chunk))\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end(expectedRequestBody)\n  })\n\n  t.plan(2)\n\n  server.listen()\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'POST',\n      dispatcher: client,\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text-plain'\n      },\n      body: expectedBody\n    }\n  )\n\n  const responseBody = await response.text()\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  t.assert.strictEqual(Buffer.concat(requestBody).toString('utf-8'), expectedBody)\n  t.assert.strictEqual(responseBody, expectedRequestBody)\n})\n\n// Skipping for now, there is something odd in the way the body is handled\ntest(\n  '[Fetch] Should handle h2 request with body (stream)',\n  async (t) => {\n    const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n    const expectedBody = readFileSync(__filename, 'utf-8')\n    const stream = createReadStream(__filename)\n    const requestChunks = []\n\n    t.plan(8)\n\n    server.on('stream', async (stream, headers) => {\n      t.assert.strictEqual(headers[':method'], 'PUT')\n      t.assert.strictEqual(headers[':path'], '/')\n      t.assert.strictEqual(headers[':scheme'], 'https')\n\n      stream.respond({\n        'content-type': 'text/plain; charset=utf-8',\n        'x-custom-h2': headers['x-my-header'],\n        ':status': 200\n      })\n\n      for await (const chunk of stream) {\n        requestChunks.push(chunk)\n      }\n\n      stream.end('hello h2!')\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    t.after(closeClientAndServerAsPromise(client, server))\n\n    const response = await fetch(\n      `https://localhost:${server.address().port}/`,\n      // Needs to be passed to disable the reject unauthorized\n      {\n        method: 'PUT',\n        dispatcher: client,\n        headers: {\n          'x-my-header': 'foo',\n          'content-type': 'text-plain'\n        },\n        body: Readable.toWeb(stream),\n        duplex: 'half'\n      }\n    )\n\n    const responseBody = await response.text()\n\n    t.assert.strictEqual(response.status, 200)\n    t.assert.strictEqual(response.headers.get('content-type'), 'text/plain; charset=utf-8')\n    t.assert.strictEqual(response.headers.get('x-custom-h2'), 'foo')\n    t.assert.strictEqual(responseBody, 'hello h2!')\n    t.assert.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n  }\n)\ntest('Should handle h2 request with body (Blob)', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'asd'\n  const requestChunks = []\n  const body = new Blob(['asd'], {\n    type: 'text/plain'\n  })\n\n  t.plan(8)\n\n  server.on('stream', async (stream, headers) => {\n    t.assert.strictEqual(headers[':method'], 'POST')\n    t.assert.strictEqual(headers[':path'], '/')\n    t.assert.strictEqual(headers[':scheme'], 'https')\n\n    stream.on('data', chunk => requestChunks.push(chunk))\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end('hello h2!')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      body,\n      method: 'POST',\n      dispatcher: client,\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text-plain'\n      }\n    }\n  )\n\n  const responseBody = await response.arrayBuffer()\n\n  t.assert.strictEqual(response.status, 200)\n  t.assert.strictEqual(response.headers.get('content-type'), 'text/plain; charset=utf-8')\n  t.assert.strictEqual(response.headers.get('x-custom-h2'), 'foo')\n  t.assert.strictEqual(new TextDecoder().decode(responseBody).toString(), 'hello h2!')\n  t.assert.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n})\n\ntest(\n  'Should handle h2 request with body (Blob:ArrayBuffer)',\n  async (t) => {\n    const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n    const expectedBody = 'hello'\n    const requestChunks = []\n    const expectedResponseBody = { hello: 'h2' }\n    const buf = Buffer.from(expectedBody)\n    const body = new ArrayBuffer(buf.byteLength)\n\n    buf.copy(new Uint8Array(body))\n\n    t.plan(8)\n\n    server.on('stream', async (stream, headers) => {\n      t.assert.strictEqual(headers[':method'], 'PUT')\n      t.assert.strictEqual(headers[':path'], '/')\n      t.assert.strictEqual(headers[':scheme'], 'https')\n\n      stream.on('data', chunk => requestChunks.push(chunk))\n\n      stream.respond({\n        'content-type': 'application/json',\n        'x-custom-h2': headers['x-my-header'],\n        ':status': 200\n      })\n\n      stream.end(JSON.stringify(expectedResponseBody))\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    t.after(closeClientAndServerAsPromise(client, server))\n\n    const response = await fetch(\n      `https://localhost:${server.address().port}/`,\n      // Needs to be passed to disable the reject unauthorized\n      {\n        body,\n        method: 'PUT',\n        dispatcher: client,\n        headers: {\n          'x-my-header': 'foo',\n          'content-type': 'text-plain'\n        }\n      }\n    )\n\n    const responseBody = await response.json()\n\n    t.assert.strictEqual(response.status, 200)\n    t.assert.strictEqual(response.headers.get('content-type'), 'application/json')\n    t.assert.strictEqual(response.headers.get('x-custom-h2'), 'foo')\n    t.assert.deepStrictEqual(responseBody, expectedResponseBody)\n    t.assert.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n  }\n)\n\ntest('Issue#2415', async (t) => {\n  t.plan(1)\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      ':status': 200\n    })\n    stream.end('test')\n  })\n\n  server.listen()\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'GET',\n      dispatcher: client\n    }\n  )\n\n  await response.text()\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  t.assert.doesNotThrow(() => new Headers(response.headers))\n})\n\ntest('Issue #2386', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const body = Buffer.from('hello')\n  const requestChunks = []\n  const expectedResponseBody = { hello: 'h2' }\n  const controller = new AbortController()\n  const signal = controller.signal\n\n  t.plan(4)\n\n  server.on('stream', async (stream, headers) => {\n    t.assert.strictEqual(headers[':method'], 'PUT')\n    t.assert.strictEqual(headers[':path'], '/')\n    t.assert.strictEqual(headers[':scheme'], 'https')\n\n    stream.on('data', chunk => requestChunks.push(chunk))\n\n    stream.respond({\n      'content-type': 'application/json',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end(JSON.stringify(expectedResponseBody))\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      body,\n      signal,\n      method: 'PUT',\n      dispatcher: client,\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text-plain'\n      }\n    }\n  )\n\n  controller.abort()\n  t.assert.ok(true)\n})\n\ntest('Issue #3046', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  t.plan(6)\n\n  server.on('stream', async (stream, headers) => {\n    t.assert.strictEqual(headers[':method'], 'GET')\n    t.assert.strictEqual(headers[':path'], '/')\n    t.assert.strictEqual(headers[':scheme'], 'https')\n\n    stream.respond({\n      'set-cookie': ['hello=world', 'foo=bar'],\n      'content-type': 'text/html; charset=utf-8',\n      ':status': 200\n    })\n\n    stream.end('<h1>Hello World</h1>')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'GET',\n      dispatcher: client\n    }\n  )\n\n  t.assert.strictEqual(response.status, 200)\n  t.assert.strictEqual(response.headers.get('content-type'), 'text/html; charset=utf-8')\n  t.assert.deepStrictEqual(response.headers.getSetCookie(), ['hello=world', 'foo=bar'])\n})\n\n// The two following tests ensure that empty POST requests have a Content-Length of 0\n// specified, both with and without HTTP/2 enabled.\n// The RFC 9110 (see https://httpwg.org/specs/rfc9110.html#field.content-length)\n// states it SHOULD have one for methods like POST that define a meaning for enclosed content.\ntest('[Fetch] Empty POST without h2 has Content-Length', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    res.end(`content-length:${req.headers['content-length']}`)\n  }).listen(0)\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n\n  t.after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  t.plan(1)\n\n  await once(server, 'listening')\n\n  const response = await fetch(\n    `http://localhost:${server.address().port}/`, {\n      method: 'POST',\n      dispatcher: client\n    }\n  )\n\n  const responseBody = await response.text()\n  t.assert.strictEqual(responseBody, `content-length:${0}`)\n})\n\ntest('[Fetch] Empty POST with h2 has Content-Length', async (t) => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      ':status': 200\n    })\n\n    stream.end(`content-length:${headers['content-length']}`)\n  })\n\n  t.plan(1)\n\n  server.listen()\n  await once(server, 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t.after(closeClientAndServerAsPromise(client, server))\n\n  const response = await fetch(\n    `https://localhost:${server.address().port}/`,\n    // Needs to be passed to disable the reject unauthorized\n    {\n      method: 'POST',\n      dispatcher: client\n    }\n  )\n\n  const responseBody = await response.text()\n\n  t.assert.strictEqual(responseBody, `content-length:${0}`)\n})\n"
  },
  {
    "path": "test/fetch/includes-credentials.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { includesCredentials } = require('../../lib/web/fetch/util')\n\ntest('includesCredentials returns true for URL with both username and password', () => {\n  const url = new URL('http://user:pass@example.com')\n  require('node:assert').strictEqual(includesCredentials(url), true)\n})\n\ntest('includesCredentials returns true for URL with only username', () => {\n  const url = new URL('http://user@example.com')\n  require('node:assert').strictEqual(includesCredentials(url), true)\n})\n\ntest('includesCredentials returns true for URL with only password', () => {\n  const url = new URL('http://:pass@example.com')\n  require('node:assert').strictEqual(includesCredentials(url), true)\n})\n\ntest('includesCredentials returns false for URL with no credentials', () => {\n  const url = new URL('http://example.com')\n  require('node:assert').strictEqual(includesCredentials(url), false)\n})\n"
  },
  {
    "path": "test/fetch/integrity.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { gzipSync } = require('node:zlib')\nconst { fetch, setGlobalDispatcher, getGlobalDispatcher, Agent } = require('../..')\nconst { once } = require('node:events')\nconst { closeServerAsPromise } = require('../utils/node-http')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\n\nconst previousDispatcher = getGlobalDispatcher()\nsetGlobalDispatcher(new Agent({\n  keepAliveTimeout: 1,\n  keepAliveMaxTimeout: 1\n}))\n\nafter(() => {\n  setGlobalDispatcher(previousDispatcher)\n})\n\nconst skip = runtimeFeatures.has('crypto') === false\n\ntest('request with correct integrity checksum', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${hash}`\n  })\n  t.assert.strictEqual(body, await response.text())\n})\n\ntest('request with wrong integrity checksum', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = 'c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51b'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const expectedError = new TypeError('fetch failed', {\n    cause: new Error('integrity mismatch')\n  })\n\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${hash}`\n  }), expectedError)\n})\n\ntest('request with integrity checksum on encoded body', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-encoding', 'gzip')\n    res.end(gzipSync(body))\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${hash}`\n  })\n  t.assert.strictEqual(body, await response.text())\n})\n\ntest('request with a totally incorrect integrity', { skip }, async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  await t.assert.doesNotReject(fetch(`http://localhost:${server.address().port}`, {\n    integrity: 'what-integrityisthis'\n  }))\n})\n\ntest('request with mixed in/valid integrities', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  await t.assert.doesNotReject(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `invalid-integrity sha256-${hash}`\n  }))\n})\n\ntest('request with sha384 hash', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY/JV615lypu7hkHMz+'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  // request should succeed\n  await t.assert.doesNotReject(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${hash}`\n  }))\n\n  // request should fail\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: 'sha384-ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n  }))\n})\n\ntest('request with sha512 hash', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash = '9s3ioPgZMUzd5V/CJ9jX2uPSjMVWIioKitZtkcytSq1glPUXohgjYMmqz2o9wyMWLLb9jN/+2w/gOPVehf+1tg=='\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  // request should succeed\n  await t.assert.doesNotReject(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha512-${hash}`\n  }))\n\n  // request should fail\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: 'sha512-ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n  }))\n})\n\ntest('request with sha512 hash', { skip }, async (t) => {\n  const body = 'Hello world!'\n  const hash384 = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY/JV615lypu7hkHMz+'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  // request should fail\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha512-${hash384} sha384-${hash384}`\n  }))\n})\n\ntest('request with correct integrity checksum (base64url)', { skip }, async (t) => {\n  t.plan(1)\n  const body = 'Hello world!'\n  const hash = 'wFNeS-K3n_2TKRMFQ2v4iTFOSj-uwF7P_Lt98xrZ5Ro'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${hash}`\n  })\n  t.assert.strictEqual(body, await response.text())\n})\n\ntest('request with incorrect integrity checksum (base64url)', { skip }, async (t) => {\n  t.plan(1)\n\n  const body = 'Hello world!'\n  // base64url for 'invalid' sha256\n  const hash = '8SNNdReNiSoTOkEDVaWpkM910vM-uiXVdZQ9TfYy86Q'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${hash}`\n  }))\n})\n\ntest('request with incorrect integrity checksum (only dash)', { skip }, async (t) => {\n  t.plan(1)\n\n  const body = 'Hello world!'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  await t.assert.rejects(fetch(`http://localhost:${server.address().port}`, {\n    integrity: 'sha256--'\n  }))\n})\n\ntest('request with incorrect integrity checksum (non-ascii character)', { skip }, async (t) => {\n  t.plan(1)\n\n  const body = 'Hello world!'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  await t.assert.rejects(() => fetch(`http://localhost:${server.address().port}`, {\n    integrity: 'sha256-ä'\n  }))\n})\n\ntest('request with incorrect stronger integrity checksum (non-ascii character)', { skip }, async (t) => {\n  t.plan(2)\n\n  const body = 'Hello world!'\n  const sha256 = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n  const sha384 = 'ä'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  await t.assert.rejects(() => fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${sha256} sha384-${sha384}`\n  }))\n  await t.assert.rejects(() => fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha256-${sha256}`\n  }))\n})\n\ntest('request with correct integrity checksum (base64). mixed', { skip }, async (t) => {\n  t.plan(6)\n  const body = 'Hello world!'\n  const sha256 = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n  const sha384 = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY/JV615lypu7hkHMz+'\n  const sha512 = '9s3ioPgZMUzd5V/CJ9jX2uPSjMVWIioKitZtkcytSq1glPUXohgjYMmqz2o9wyMWLLb9jN/+2w/gOPVehf+1tg=='\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  let response\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${sha256} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha512-${sha512} sha256-${sha256}`\n  })\n\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${sha256} sha384-${sha384}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha256-${sha256}`\n  })\n  t.assert.strictEqual(body, await response.text())\n})\n\ntest('request with correct integrity checksum (base64url). mixed', { skip }, async (t) => {\n  t.plan(6)\n  const body = 'Hello world!'\n  const sha256 = 'wFNeS-K3n_2TKRMFQ2v4iTFOSj-uwF7P_Lt98xrZ5Ro'\n  const sha384 = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY_JV615lypu7hkHMz-'\n  const sha512 = '9s3ioPgZMUzd5V_CJ9jX2uPSjMVWIioKitZtkcytSq1glPUXohgjYMmqz2o9wyMWLLb9jN_-2w_gOPVehf-1tg'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(body)\n  })\n\n  after(closeServerAsPromise(server))\n\n  await once(server.listen(0), 'listening')\n  let response\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${sha256} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha512-${sha512} sha256-${sha256}`\n  })\n\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha512-${sha512}`\n  })\n  t.assert.strictEqual(body, await response.text())\n\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha256-${sha256} sha384-${sha384}`\n  })\n  t.assert.strictEqual(body, await response.text())\n  response = await fetch(`http://localhost:${server.address().port}`, {\n    integrity: `sha384-${sha384} sha256-${sha256}`\n  })\n  t.assert.strictEqual(body, await response.text())\n})\n"
  },
  {
    "path": "test/fetch/issue-1447.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\n\nconst undici = require('../..')\nconst { fetch: theoreticalGlobalFetch } = require('../../undici-fetch')\n\ntest('Mocking works with both fetches', async (t) => {\n  t.plan(3)\n\n  const mockAgent = new undici.MockAgent()\n  const body = JSON.stringify({ foo: 'bar' })\n\n  mockAgent.disableNetConnect()\n  const previousDispatcher = undici.getGlobalDispatcher()\n  undici.setGlobalDispatcher(mockAgent)\n  t.after(() => {\n    undici.setGlobalDispatcher(previousDispatcher)\n  })\n  const pool = mockAgent.get('https://example.com')\n\n  pool.intercept({\n    path: '/path',\n    method: 'POST',\n    body (bodyString) {\n      t.assert.strictEqual(bodyString, body)\n      return true\n    }\n  }).reply(200, { ok: 1 }).times(2)\n\n  const url = new URL('https://example.com/path').href\n\n  // undici fetch from node_modules\n  await undici.fetch(url, {\n    method: 'POST',\n    body\n  })\n\n  // the global fetch bundled with esbuild\n  await theoreticalGlobalFetch(url, {\n    method: 'POST',\n    body\n  })\n})\n"
  },
  {
    "path": "test/fetch/issue-1711.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\ntest('Redirecting a bunch does not cause a MaxListenersExceededWarning', async (t) => {\n  let redirects = 0\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (redirects === 15) {\n      res.end('Okay goodbye')\n      return\n    }\n\n    res.writeHead(302, {\n      Location: `/${redirects++}`\n    })\n    res.end()\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  process.emitWarning = t.assert.fail.bind(t)\n\n  const url = `http://localhost:${server.address().port}`\n  const response = await fetch(url, { redirect: 'follow' })\n\n  t.assert.deepStrictEqual(response.url, `${url}/${redirects - 1}`)\n})\n\ntest(\n  'aborting a Stream throws',\n  () => {\n    return new Promise((resolve, reject) => {\n      const httpServer = createServer({ joinDuplicateHeaders: true }, (request, response) => {\n        response.end(new Uint8Array(20000))\n      }).listen(async () => {\n        const serverAddress = httpServer.address()\n\n        if (typeof serverAddress === 'object') {\n          const abortController = new AbortController()\n          const readStream = (await fetch(`http://localhost:${serverAddress?.port}`, { signal: abortController.signal })).arrayBuffer()\n          abortController.abort()\n          setTimeout(reject)\n\n          try {\n            await readStream\n          } catch {\n            httpServer.close()\n            resolve()\n          }\n        }\n      })\n    })\n  }\n)\n"
  },
  {
    "path": "test/fetch/issue-2009.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('issue 2009', async (t) => {\n  t.plan(10)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('a', 'b')\n    res.flushHeaders()\n\n    res.socket.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  for (let i = 0; i < 10; i++) {\n    await t.assert.doesNotReject(\n      fetch(`http://localhost:${server.address().port}`).then(\n        async (resp) => {\n          await resp.body.cancel('Some message')\n        }\n      )\n    )\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-2021.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { fetch } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// https://github.com/nodejs/undici/issues/2021\ntest('content-length header is removed on redirect', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/redirect') {\n      res.writeHead(302, { Location: '/redirect2' })\n      res.end()\n      return\n    }\n\n    res.end()\n  }).listen(0).unref()\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const body = 'a+b+c'\n\n  await t.assert.doesNotReject(fetch(`http://localhost:${server.address().port}/redirect`, {\n    method: 'POST',\n    body,\n    headers: {\n      'content-length': Buffer.byteLength(body)\n    }\n  }))\n})\n"
  },
  {
    "path": "test/fetch/issue-2171.js",
    "content": "'use strict'\n\nconst { fetch } = require('../..')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('error reason is forwarded - issue #2171', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, () => {}).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const timeout = AbortSignal.timeout(100)\n  await t.assert.rejects(\n    fetch(`http://localhost:${server.address().port}`, {\n      signal: timeout\n    }),\n    {\n      name: 'TimeoutError',\n      code: DOMException.TIMEOUT_ERR\n    }\n  )\n})\n"
  },
  {
    "path": "test/fetch/issue-2242.js",
    "content": "'use strict'\n\nconst { beforeEach, describe, it } = require('node:test')\nconst { fetch } = require('../..')\nconst nodeFetch = require('../../index-fetch')\n\ndescribe('Issue #2242', () => {\n  ['Already aborted', null, false, true, 123, Symbol('Some reason')].forEach(\n    (reason) =>\n      describe(`when an already-aborted signal's reason is \\`${String(\n        reason\n      )}\\``, () => {\n        let signal\n        beforeEach(() => {\n          signal = AbortSignal.abort(reason)\n        })\n        it('rejects with that reason ', async (t) => {\n          await t.assert.rejects(fetch('http://localhost', { signal }), (err) => {\n            t.assert.strictEqual(err, reason)\n            return true\n          })\n        })\n        it('rejects with that reason (from index-fetch)', async (t) => {\n          await t.assert.rejects(\n            nodeFetch.fetch('http://localhost', { signal }),\n            (err) => {\n              t.assert.strictEqual(err, reason)\n              return true\n            }\n          )\n        })\n      })\n  )\n\n  describe(\"when an already-aborted signal's reason is `undefined`\", () => {\n    let signal\n    beforeEach(() => {\n      signal = AbortSignal.abort(undefined)\n    })\n    it('rejects with an `AbortError`', async (t) => {\n      await t.assert.rejects(\n        fetch('http://localhost', { signal }),\n        new DOMException('This operation was aborted', 'AbortError')\n      )\n    })\n    it('rejects with an `AbortError` (from index-fetch)', async (t) => {\n      await t.assert.rejects(\n        nodeFetch.fetch('http://localhost', { signal }),\n        new DOMException('This operation was aborted', 'AbortError')\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "test/fetch/issue-2294-patch-method.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { Request } = require('../..')\n\ntest('Using `patch` method emits a warning.', (t) => {\n  t.plan(1)\n\n  const { emitWarning } = process\n\n  after(() => {\n    process.emitWarning = emitWarning\n  })\n\n  process.emitWarning = (warning, options) => {\n    t.assert.strictEqual(options.code, 'UNDICI-FETCH-patch')\n  }\n\n  // eslint-disable-next-line no-new\n  new Request('https://a', { method: 'patch' })\n})\n"
  },
  {
    "path": "test/fetch/issue-2318.js",
    "content": "'use strict'\r\n\r\nconst { test } = require('node:test')\r\nconst { once } = require('node:events')\r\nconst { createServer } = require('node:http')\r\nconst { fetch } = require('../..')\r\nconst { closeServerAsPromise } = require('../utils/node-http')\r\n\r\ntest('Undici overrides user-provided `Host` header', async (t) => {\r\n  t.plan(1)\r\n\r\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\r\n    t.assert.strictEqual(req.headers.host, `localhost:${server.address().port}`)\r\n\r\n    res.end()\r\n  }).listen(0)\r\n\r\n  t.after(closeServerAsPromise(server))\r\n  await once(server, 'listening')\r\n\r\n  await fetch(`http://localhost:${server.address().port}`, {\r\n    headers: {\r\n      host: 'www.idk.org'\r\n    }\r\n  })\r\n})\r\n"
  },
  {
    "path": "test/fetch/issue-2828.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { Agent, Request, fetch } = require('../..')\n\ntest('issue #2828, dispatcher is allowed in RequestInit options', async (t) => {\n  t.plan(1)\n\n  class CustomAgent extends Agent {\n    dispatch (options, handler) {\n      options.headers['x-my-header'] = 'hello'\n      return super.dispatch(...arguments)\n    }\n  }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.deepStrictEqual(req.headers['x-my-header'], 'hello')\n    res.end()\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  const request = new Request(`http://localhost:${server.address().port}`, {\n    dispatcher: new CustomAgent()\n  })\n\n  await fetch(request)\n})\n"
  },
  {
    "path": "test/fetch/issue-2898-comment.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { Agent, Request, fetch } = require('../..')\n\ntest('issue #2828, RequestInit dispatcher options overrides Request input dispatcher', async (t) => {\n  t.plan(2)\n\n  class CustomAgentA extends Agent {\n    dispatch (options, handler) {\n      options.headers['x-my-header-a'] = 'hello'\n      return super.dispatch(...arguments)\n    }\n  }\n\n  class CustomAgentB extends Agent {\n    dispatch (options, handler) {\n      options.headers['x-my-header-b'] = 'world'\n      return super.dispatch(...arguments)\n    }\n  }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.headers['x-my-header-a'], undefined)\n    t.assert.strictEqual(req.headers['x-my-header-b'], 'world')\n    res.end()\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  const request = new Request(`http://localhost:${server.address().port}`, {\n    dispatcher: new CustomAgentA()\n  })\n\n  await fetch(request, {\n    dispatcher: new CustomAgentB()\n  })\n})\n"
  },
  {
    "path": "test/fetch/issue-2898.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\n// https://github.com/nodejs/undici/issues/2898\ntest('421 requests with a body work as expected', async (t) => {\n  const expected = 'This is a 421 Misdirected Request response.'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 421\n    res.end(expected)\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  for (const body of [\n    'hello',\n    new Uint8Array(Buffer.from('helloworld', 'utf-8'))\n  ]) {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'POST',\n      body\n    })\n\n    t.assert.deepStrictEqual(response.status, 421)\n    t.assert.deepStrictEqual(await response.text(), expected)\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-3267.js",
    "content": "'use strict'\n\nconst { Headers } = require('../..')\nconst { test } = require('node:test')\n\ntest('Spreading a Headers object yields 0 symbols', (t) => {\n  const baseHeaders = { 'x-foo': 'bar' }\n\n  const requestHeaders = new Headers({ 'Content-Type': 'application/json' })\n  const headers = {\n    ...baseHeaders,\n    ...requestHeaders\n  }\n\n  t.assert.deepStrictEqual(headers, { 'x-foo': 'bar' })\n  t.assert.doesNotThrow(() => new Headers(headers))\n})\n"
  },
  {
    "path": "test/fetch/issue-3334.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { fetch } = require('../..')\n\ntest('a non-empty origin is not appended (issue #3334)', async (t) => {\n  t.plan(1)\n  const origin = 'https://origin.example.com'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.headers.origin, origin)\n    res.end()\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  await fetch(`http://localhost:${server.address().port}`, {\n    headers: { origin },\n    body: '',\n    method: 'POST',\n    redirect: 'error'\n  })\n})\n"
  },
  {
    "path": "test/fetch/issue-3616.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { describe, test, after } = require('node:test')\nconst { fetch } = require('../..')\nconst { once } = require('node:events')\n\ndescribe('https://github.com/nodejs/undici/issues/3616', () => {\n  const cases = [\n    'x-gzip',\n    'gzip',\n    'deflate',\n    'br'\n  ]\n\n  for (const encoding of cases) {\n    test(encoding, async t => {\n      t.plan(2)\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        res.writeHead(200, {\n          'Content-Length': '0',\n          Connection: 'close',\n          'Content-Encoding': encoding\n        })\n        res.end()\n      })\n\n      after(() => {\n        server.close()\n      })\n\n      server.listen(0)\n\n      await once(server, 'listening')\n      const result = await fetch(`http://localhost:${server.address().port}/`)\n\n      t.assert.ok(result.body.getReader())\n\n      process.on('uncaughtException', (reason) => {\n        t.assert.fail('Uncaught Exception:', reason, encoding)\n      })\n\n      await new Promise(resolve => setTimeout(resolve, 100))\n      t.assert.ok(true)\n    })\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-3624.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { fetch, FormData } = require('../..')\n\n// https://github.com/nodejs/undici/issues/3624\ntest('crlf is appended to formdata body (issue #3624)', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.pipe(res)\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  const fd = new FormData()\n  fd.set('a', 'b')\n  fd.set('c', new File(['d'], 'd.txt.exe'), 'd.txt.exe')\n\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    body: fd,\n    method: 'POST'\n  })\n\n  t.assert.ok((await response.text()).endsWith('\\r\\n'))\n})\n"
  },
  {
    "path": "test/fetch/issue-3630.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Request, Agent } = require('../..')\nconst { getRequestDispatcher } = require('../../lib/web/fetch/request')\n\ntest('Cloned request should inherit its dispatcher', (t) => {\n  const agent = new Agent()\n  const request = new Request('https://a', { dispatcher: agent })\n  t.assert.strictEqual(getRequestDispatcher(request), agent)\n})\n"
  },
  {
    "path": "test/fetch/issue-3767.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\n// https://github.com/nodejs/undici/issues/3767\ntest('referrerPolicy unsafe-url is respected', async (t) => {\n  t.plan(1)\n\n  const referrer = 'https://google.com/hello/world'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.deepEqual(req.headers.referer, referrer)\n\n    res.end()\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  await fetch(`http://localhost:${server.address().port}`, {\n    referrer,\n    referrerPolicy: 'unsafe-url'\n  })\n})\n"
  },
  {
    "path": "test/fetch/issue-4105.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { PerformanceObserver } = require('node:perf_hooks')\nconst { createDeferredPromise } = require('../../lib/util/promise')\n\nconst isAtLeastv22 = process.versions.node.split('.').map(Number)[0] >= 22\n\n// https://github.com/nodejs/undici/issues/4105\ntest('markResourceTiming responseStatus is set', { skip: !isAtLeastv22 }, async (t) => {\n  t.plan(1)\n\n  const promise = createDeferredPromise()\n\n  const server = createServer((req, res) => {\n    res.statusCode = 200\n    res.end('Hello World')\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  new PerformanceObserver(items => {\n    items.getEntries().forEach(entry => {\n      t.assert.strictEqual(entry.responseStatus, 200)\n      promise.resolve()\n    })\n  }).observe({ type: 'resource', buffered: true })\n\n  const response = await fetch(`http://localhost:${server.address().port}`)\n  await response.text()\n\n  await promise.promise\n})\n"
  },
  {
    "path": "test/fetch/issue-4627.js",
    "content": "'use strict'\n\n// Regression test for https://github.com/nodejs/undici/issues/4627\n// Fetch abort may not take effect when fetch init.redirect = 'error'\n// causing SSE connection leak\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// This test requires --expose-gc flag\nconst hasGC = typeof global.gc === 'function'\n\ntest('abort should work with redirect: error', { skip: !hasGC, timeout: 3000 }, async (t) => {\n  let connectionClosed = false\n  let messagesReceivedAfterAbort = 0\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/event-stream',\n      'Cache-Control': 'no-cache',\n      Connection: 'keep-alive'\n    })\n\n    // Send data every 20ms for faster test\n    const interval = setInterval(() => {\n      res.write(`data: ${Date.now()}\\n\\n`)\n    }, 20)\n\n    res.on('close', () => {\n      connectionClosed = true\n      clearInterval(interval)\n    })\n  })\n\n  t.after(closeServerAsPromise(server))\n  await once(server.listen(0), 'listening')\n  const port = server.address().port\n\n  const ac = new AbortController()\n\n  const response = await fetch(`http://localhost:${port}/sse`, {\n    signal: ac.signal,\n    redirect: 'error'\n  })\n\n  let aborted = false\n\n  // Start reading the stream in background\n  const readPromise = (async () => {\n    try {\n      const reader = response.body.getReader()\n      while (true) {\n        const { done } = await reader.read()\n        if (done) break\n\n        if (aborted) {\n          messagesReceivedAfterAbort++\n          if (messagesReceivedAfterAbort >= 3) {\n            // Bug confirmed - received multiple messages after abort\n            reader.cancel()\n            break\n          }\n        }\n      }\n    } catch (err) {\n      // AbortError is expected\n      if (err.name !== 'AbortError' && err.message !== 'aborted' && !err.message?.includes('cancel')) {\n        throw err\n      }\n    }\n  })()\n\n  // Wait for some data to be received\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  // Trigger GC to potentially collect the AbortController\n  global.gc()\n\n  // Abort the request\n  aborted = true\n  ac.abort()\n\n  // Wait for the read to complete or timeout\n  const timeout = new Promise((_resolve, reject) =>\n    setTimeout(() => reject(new Error('Read did not complete in time')), 1000)\n  )\n\n  try {\n    await Promise.race([readPromise, timeout])\n  } catch (e) {\n    // If timed out, that's also a bug indication\n    if (e.message === 'Read did not complete in time') {\n      messagesReceivedAfterAbort = 999 // Force failure\n    } else {\n      throw e\n    }\n  }\n\n  t.assert.strictEqual(messagesReceivedAfterAbort, 0, 'No data should be received after abort')\n\n  // Give some time for the connection to close\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  t.assert.ok(connectionClosed, 'Server connection should be closed after abort')\n})\n\ntest('abort should work without redirect: error (control test)', { skip: !hasGC, timeout: 3000 }, async (t) => {\n  let connectionClosed = false\n  let messagesReceivedAfterAbort = 0\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/event-stream',\n      'Cache-Control': 'no-cache',\n      Connection: 'keep-alive'\n    })\n\n    // Send data every 20ms\n    const interval = setInterval(() => {\n      res.write(`data: ${Date.now()}\\n\\n`)\n    }, 20)\n\n    res.on('close', () => {\n      connectionClosed = true\n      clearInterval(interval)\n    })\n  })\n\n  t.after(closeServerAsPromise(server))\n  await once(server.listen(0), 'listening')\n  const port = server.address().port\n\n  const ac = new AbortController()\n\n  // Without redirect: 'error' - this should work correctly\n  const response = await fetch(`http://localhost:${port}/sse`, {\n    signal: ac.signal\n  })\n\n  let aborted = false\n\n  // Start reading the stream in background\n  const readPromise = (async () => {\n    try {\n      const reader = response.body.getReader()\n      while (true) {\n        const { done } = await reader.read()\n        if (done) break\n\n        if (aborted) {\n          messagesReceivedAfterAbort++\n          if (messagesReceivedAfterAbort >= 3) {\n            reader.cancel()\n            break\n          }\n        }\n      }\n    } catch (err) {\n      // AbortError is expected\n      if (err.name !== 'AbortError' && err.message !== 'aborted' && !err.message?.includes('cancel')) {\n        throw err\n      }\n    }\n  })()\n\n  // Wait for some data to be received\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  // Trigger GC\n  global.gc()\n\n  // Abort the request\n  aborted = true\n  ac.abort()\n\n  // Wait for the read to complete\n  await readPromise\n\n  // Give some time for the connection to close\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  t.assert.strictEqual(messagesReceivedAfterAbort, 0, 'No data should be received after abort')\n  t.assert.ok(connectionClosed, 'Server connection should be closed after abort')\n})\n"
  },
  {
    "path": "test/fetch/issue-4647.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\n// https://github.com/nodejs/undici/issues/4647\ntest('fetch with mode: no-cors does not hang', async (t) => {\n  const a = createServer((req, res) => {\n    res.writeHead(200).end()\n  }).listen(0)\n\n  const b = createServer((req, res) => {\n    res.writeHead(301, { Location: `http://localhost:${a.address().port}${req.url}` }).end()\n  }).listen(0)\n\n  t.after(() => {\n    a.close()\n    b.close()\n  })\n\n  await fetch(`http://localhost:${b.address().port}/abc`, { mode: 'no-cors' })\n})\n"
  },
  {
    "path": "test/fetch/issue-4789.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { fetch } = require('../..')\n\n// https://github.com/nodejs/undici/issues/4789\ntest('transferred buffers and extractBody works', { skip: !ArrayBuffer.prototype.transfer }, async (t) => {\n  const server = createServer((req, res) => {\n    if (req.url === '/') {\n      res.writeHead(307, undefined, {\n        location: '/test'\n      })\n      res.end()\n      return\n    }\n\n    req.pipe(res).on('end', res.end.bind(res))\n  }).listen(0)\n\n  t.after(server.close.bind(server))\n  await once(server, 'listening')\n\n  {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'POST',\n      body: new TextEncoder().encode('test')\n    })\n\n    t.assert.strictEqual(await response.text(), 'test')\n  }\n\n  {\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'POST',\n      body: Buffer.from('test')\n    })\n\n    t.assert.strictEqual(await response.text(), 'test')\n  }\n\n  {\n    const buffer = new TextEncoder().encode('test')\n    buffer.buffer.transfer()\n\n    const response = await fetch(`http://localhost:${server.address().port}`, {\n      method: 'POST',\n      body: buffer\n    })\n\n    // https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy\n    // \"If IsDetachedBuffer(jsArrayBuffer) is true, then return the empty byte sequence.\"\n    t.assert.strictEqual(await response.text(), '')\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-4799.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('response clone + abort should return AbortError, not TypeError', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify({ message: 'hello' }))\n  })\n\n  t.after(closeServerAsPromise(server))\n  server.listen(0)\n  await once(server, 'listening')\n\n  const controller = new AbortController()\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    signal: controller.signal\n  })\n\n  // Clone the response before aborting\n  const clonedResponse = response.clone()\n\n  // Abort after cloning\n  controller.abort()\n\n  // Both original and cloned response should reject with AbortError\n  await t.test('original response should reject with AbortError', async () => {\n    await t.assert.rejects(\n      response.text(),\n      {\n        name: 'AbortError',\n        code: DOMException.ABORT_ERR\n      }\n    )\n  })\n\n  await t.test('cloned response should reject with AbortError', async () => {\n    await t.assert.rejects(\n      clonedResponse.text(),\n      {\n        name: 'AbortError',\n        code: DOMException.ABORT_ERR\n      }\n    )\n  })\n})\n\ntest('response without clone + abort should still return AbortError', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify({ message: 'hello' }))\n  })\n\n  t.after(closeServerAsPromise(server))\n  server.listen(0)\n  await once(server, 'listening')\n\n  const controller = new AbortController()\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    signal: controller.signal\n  })\n\n  // Abort without cloning\n  controller.abort()\n\n  await t.assert.rejects(\n    response.text(),\n    {\n      name: 'AbortError',\n      code: DOMException.ABORT_ERR\n    }\n  )\n})\n\ntest('response bodyUsed after clone and abort', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    res.setHeader('Content-Type', 'application/json')\n    res.end(JSON.stringify({ message: 'hello' }))\n  })\n\n  t.after(closeServerAsPromise(server))\n  server.listen(0)\n  await once(server, 'listening')\n\n  const controller = new AbortController()\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    signal: controller.signal\n  })\n\n  t.assert.strictEqual(response.bodyUsed, false)\n\n  const clonedResponse = response.clone()\n\n  t.assert.strictEqual(response.bodyUsed, false)\n  t.assert.strictEqual(clonedResponse.bodyUsed, false)\n\n  controller.abort()\n\n  // Erroring a stream (see: https://fetch.spec.whatwg.org/#abort-fetch step 5) does not disturb it.\n  t.assert.strictEqual(response.bodyUsed, false)\n  t.assert.strictEqual(clonedResponse.bodyUsed, false)\n})\n"
  },
  {
    "path": "test/fetch/issue-4836.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { fetch } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// https://github.com/nodejs/undici/issues/4836\ntest('fetch preserves trailing ? in request URL', async (t) => {\n  const server = createServer((req, res) => {\n    res.end(req.url)\n  }).listen(0)\n\n  await once(server, 'listening')\n  t.after(closeServerAsPromise(server))\n\n  const base = `http://localhost:${server.address().port}`\n\n  const cases = [\n    ['/echo', '/echo'],\n    ['/echo?', '/echo?'],\n    ['/echo?a=b', '/echo?a=b'],\n    ['/echo?a=b&c=d', '/echo?a=b&c=d'],\n    ['/echo?#frag', '/echo?'],\n    ['/echo#?', '/echo'],\n    ['/echo#frag?bar', '/echo']\n  ]\n\n  for (const [path, expected] of cases) {\n    await t.test(`path: ${path} → ${expected}`, async (t) => {\n      const res = await fetch(`${base}${path}`)\n      const body = await res.text()\n      t.assert.strictEqual(body, expected)\n    })\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-4897.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\nfunction createAssertingDispatcher (t, expectedPath) {\n  return {\n    dispatch (opts, handler) {\n      t.assert.strictEqual(opts.path, expectedPath)\n      handler.onError(new Error('stop'))\n      return true\n    }\n  }\n}\n\nasync function assertPath (t, url, expectedPath) {\n  const dispatcher = createAssertingDispatcher(t, expectedPath)\n\n  await t.assert.rejects(fetch(url, { dispatcher }), (err) => {\n    t.assert.strictEqual(err.cause?.message, 'stop')\n    return true\n  })\n}\n\n// https://github.com/nodejs/undici/issues/4897\ntest('fetch path extraction does not match hostnames inside scheme', async (t) => {\n  const hosts = ['h', 't', 'p', 'ht', 'tp', 'tt']\n\n  for (const scheme of ['http', 'https']) {\n    for (const host of hosts) {\n      await t.test(`${scheme}://${host}/test?a=b#frag`, async (t) => {\n        await assertPath(t, `${scheme}://${host}/test?a=b#frag`, '/test?a=b')\n      })\n    }\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-node-46525.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\n// https://github.com/nodejs/node/issues/46525\ntest('No warning when reusing AbortController', async (t) => {\n  function onWarning () {\n    throw new Error('Got warning')\n  }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => res.end()).listen(0)\n\n  await once(server, 'listening')\n\n  process.on('warning', onWarning)\n  t.after(() => {\n    process.off('warning', onWarning)\n    return server.close()\n  })\n\n  const controller = new AbortController()\n  for (let i = 0; i < 15; i++) {\n    await fetch(`http://localhost:${server.address().port}`, { signal: controller.signal })\n  }\n})\n"
  },
  {
    "path": "test/fetch/issue-rsshub-15532.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\n\n// https://github.com/DIYgod/RSSHub/issues/15532\ntest('An invalid Origin header is not set', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.deepStrictEqual(req.headers.origin, undefined)\n\n    res.end()\n  }).listen(0)\n\n  await once(server, 'listening')\n  t.after(server.close.bind(server))\n\n  await fetch(`http://localhost:${server.address().port}`, {\n    method: 'POST'\n  })\n})\n"
  },
  {
    "path": "test/fetch/iterators.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Headers, FormData } = require('../..')\n\ntest('Implements \" Iterator\" properly', async (t) => {\n  await t.test('all Headers iterators implement Headers Iterator', () => {\n    const headers = new Headers([['a', 'b'], ['c', 'd']])\n\n    for (const iterable of ['keys', 'values', 'entries', Symbol.iterator]) {\n      const gen = headers[iterable]()\n      // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object\n      const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))\n      const iteratorProto = Object.getPrototypeOf(gen)\n\n      t.assert.ok(gen.constructor === IteratorPrototype.constructor)\n      t.assert.ok(gen.prototype === undefined)\n      // eslint-disable-next-line no-proto\n      t.assert.strictEqual(gen.__proto__[Symbol.toStringTag], 'Headers Iterator')\n      // https://github.com/node-fetch/node-fetch/issues/1119#issuecomment-100222049\n      t.assert.ok(!(Headers.prototype[iterable] instanceof function * () {}.constructor))\n      // eslint-disable-next-line no-proto\n      t.assert.ok(gen.__proto__.next.__proto__ === Function.prototype)\n      // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object\n      // \"The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.\"\n      t.assert.strictEqual(gen[Symbol.iterator], IteratorPrototype[Symbol.iterator])\n      t.assert.strictEqual(Object.getPrototypeOf(iteratorProto), IteratorPrototype)\n    }\n  })\n\n  await t.test('all FormData iterators implement FormData Iterator', () => {\n    const fd = new FormData()\n\n    for (const iterable of ['keys', 'values', 'entries', Symbol.iterator]) {\n      const gen = fd[iterable]()\n      // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object\n      const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))\n      const iteratorProto = Object.getPrototypeOf(gen)\n\n      t.assert.ok(gen.constructor === IteratorPrototype.constructor)\n      t.assert.ok(gen.prototype === undefined)\n      // eslint-disable-next-line no-proto\n      t.assert.strictEqual(gen.__proto__[Symbol.toStringTag], 'FormData Iterator')\n      // https://github.com/node-fetch/node-fetch/issues/1119#issuecomment-100222049\n      t.assert.ok(!(Headers.prototype[iterable] instanceof function * () {}.constructor))\n      // eslint-disable-next-line no-proto\n      t.assert.ok(gen.__proto__.next.__proto__ === Function.prototype)\n      // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object\n      // \"The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.\"\n      t.assert.strictEqual(gen[Symbol.iterator], IteratorPrototype[Symbol.iterator])\n      t.assert.strictEqual(Object.getPrototypeOf(iteratorProto), IteratorPrototype)\n    }\n  })\n\n  await t.test('Iterator symbols are properly set', async (t) => {\n    await t.test('Headers', () => {\n      const headers = new Headers([['a', 'b'], ['c', 'd']])\n      const gen = headers.entries()\n\n      t.assert.strictEqual(typeof gen[Symbol.toStringTag], 'string')\n      t.assert.strictEqual(typeof gen[Symbol.iterator], 'function')\n    })\n\n    await t.test('FormData', () => {\n      const fd = new FormData()\n      const gen = fd.entries()\n\n      t.assert.strictEqual(typeof gen[Symbol.toStringTag], 'string')\n      t.assert.strictEqual(typeof gen[Symbol.iterator], 'function')\n    })\n  })\n\n  await t.test('Iterator does not inherit Generator prototype methods', async (t) => {\n    await t.test('Headers', () => {\n      const headers = new Headers([['a', 'b'], ['c', 'd']])\n      const gen = headers.entries()\n\n      t.assert.strictEqual(gen.return, undefined)\n      t.assert.strictEqual(gen.throw, undefined)\n      t.assert.strictEqual(typeof gen.next, 'function')\n    })\n\n    await t.test('FormData', () => {\n      const fd = new FormData()\n      const gen = fd.entries()\n\n      t.assert.strictEqual(gen.return, undefined)\n      t.assert.strictEqual(gen.throw, undefined)\n      t.assert.strictEqual(typeof gen.next, 'function')\n    })\n  })\n\n  await t.test('Symbol.iterator', () => {\n    // Headers\n    const headerValues = new Headers([['a', 'b']]).entries()[Symbol.iterator]()\n    t.assert.deepStrictEqual(Array.from(headerValues), [['a', 'b']])\n\n    // FormData\n    const formdata = new FormData()\n    formdata.set('a', 'b')\n    const formdataValues = formdata.entries()[Symbol.iterator]()\n    t.assert.deepStrictEqual(Array.from(formdataValues), [['a', 'b']])\n  })\n\n  await t.test('brand check', () => {\n    // Headers\n    t.assert.throws(() => {\n      const gen = new Headers().entries()\n      // eslint-disable-next-line no-proto\n      gen.__proto__.next()\n    }, TypeError)\n\n    // FormData\n    t.assert.throws(() => {\n      const gen = new FormData().entries()\n      // eslint-disable-next-line no-proto\n      gen.__proto__.next()\n    }, TypeError)\n  })\n})\n"
  },
  {
    "path": "test/fetch/long-lived-abort-controller.js",
    "content": "'use strict'\n\nconst http = require('node:http')\nconst { fetch } = require('../../')\nconst { once, setMaxListeners } = require('node:events')\nconst { test } = require('node:test')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('long-lived-abort-controller', async (t) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { 'Content-Type': 'text/plain' })\n    res.end('Hello World!')\n  })\n\n  await once(server.listen(0), 'listening')\n\n  t.after(closeServerAsPromise(server))\n\n  let emittedWarning = ''\n  function onWarning (warning) {\n    emittedWarning = warning\n  }\n  process.on('warning', onWarning)\n  t.after(() => {\n    process.off('warning', onWarning)\n  })\n\n  const controller = new AbortController()\n  setMaxListeners(1500, controller.signal)\n\n  // The maxListener is set to 1500 in request.js.\n  // we set it to 2000 to make sure that we are not leaking event listeners.\n  // Unfortunately we are relying on GC and implementation details here.\n  for (let i = 0; i < 2000; i++) {\n    // make request\n    const res = await fetch(`http://localhost:${server.address().port}`, {\n      signal: controller.signal\n    })\n\n    // drain body\n    await res.text()\n\n    // eslint-disable-next-line no-undef\n    gc()\n  }\n\n  t.assert.strictEqual(emittedWarning, '')\n})\n"
  },
  {
    "path": "test/fetch/max-listeners.js",
    "content": "'use strict'\r\n\r\nconst { setMaxListeners, getMaxListeners, defaultMaxListeners } = require('node:events')\r\nconst { test } = require('node:test')\r\nconst { Request } = require('../..')\r\n\r\ntest('test max listeners', (t) => {\r\n  const controller = new AbortController()\r\n  setMaxListeners(Infinity, controller.signal)\r\n  for (let i = 0; i <= defaultMaxListeners; i++) {\r\n    // eslint-disable-next-line no-new\r\n    new Request('http://asd', { signal: controller.signal })\r\n  }\r\n  t.assert.strictEqual(getMaxListeners(controller.signal), Infinity)\r\n})\r\n"
  },
  {
    "path": "test/fetch/pull-dont-push.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { fetch } = require('../..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { Readable, pipeline } = require('node:stream')\nconst { setTimeout: sleep } = require('node:timers/promises')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('pull dont\\'t push', async (t) => {\n  let count = 0\n  let socket\n  const max = 1_000_000\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.statusCode = 200\n    socket = res.socket\n\n    // infinite stream\n    const stream = new Readable({\n      read () {\n        this.push('a')\n        if (count++ > max) {\n          this.push(null)\n        }\n      }\n    })\n\n    pipeline(stream, res, () => {})\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const res = await fetch(`http://localhost:${server.address().port}`)\n\n  // Some time is needed to fill the buffer\n  await sleep(1000)\n\n  socket.destroy()\n  t.assert.strictEqual(count < max, true) // the stream should be closed before the max\n\n  // consume the  stream\n  try {\n    /* eslint-disable-next-line no-unused-vars */\n    for await (const chunk of res.body) {\n      // process._rawDebug('chunk', chunk)\n    }\n  } catch {}\n})\n"
  },
  {
    "path": "test/fetch/readable-stream-from.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Response } = require('../..')\n\n// https://github.com/nodejs/node/issues/56474\ntest('ReadableStream empty enqueue then other enqueued', async (t) => {\n  const iterable = {\n    async * [Symbol.asyncIterator] () {\n      yield ''\n      yield '3'\n      yield '4'\n    }\n  }\n\n  const response = new Response(iterable)\n  t.assert.deepStrictEqual(await response.text(), '34')\n})\n\ntest('ReadableStream empty enqueue', async (t) => {\n  const iterable = {\n    async * [Symbol.asyncIterator] () {\n      yield ''\n    }\n  }\n\n  const response = new Response(iterable)\n  t.assert.deepStrictEqual(await response.text(), '')\n})\n"
  },
  {
    "path": "test/fetch/redirect-cross-origin-header.js",
    "content": "'use strict'\r\n\r\nconst { test } = require('node:test')\r\nconst { createServer } = require('node:http')\r\nconst { once } = require('node:events')\r\nconst { fetch } = require('../..')\r\n\r\ntest('Cross-origin redirects clear forbidden headers', async (t) => {\r\n  t.plan(6)\r\n\r\n  const server1 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\r\n    t.assert.strictEqual(req.headers.cookie, undefined)\r\n    t.assert.strictEqual(req.headers.authorization, undefined)\r\n    t.assert.strictEqual(req.headers['proxy-authorization'], undefined)\r\n\r\n    res.end('redirected')\r\n  }).listen(0)\r\n\r\n  const server2 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\r\n    t.assert.strictEqual(req.headers.authorization, 'test')\r\n    t.assert.strictEqual(req.headers.cookie, 'ddd=dddd')\r\n\r\n    res.writeHead(302, {\r\n      ...req.headers,\r\n      Location: `http://localhost:${server1.address().port}`\r\n    })\r\n    res.end()\r\n  }).listen(0)\r\n\r\n  t.after(() => {\r\n    server1.close()\r\n    server2.close()\r\n  })\r\n\r\n  await Promise.all([\r\n    once(server1, 'listening'),\r\n    once(server2, 'listening')\r\n  ])\r\n\r\n  const res = await fetch(`http://localhost:${server2.address().port}`, {\r\n    headers: {\r\n      Authorization: 'test',\r\n      Cookie: 'ddd=dddd',\r\n      'Proxy-Authorization': 'test'\r\n    }\r\n  })\r\n\r\n  const text = await res.text()\r\n  t.assert.strictEqual(text, 'redirected')\r\n})\r\n"
  },
  {
    "path": "test/fetch/redirect.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { fetch } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// https://github.com/nodejs/undici/issues/1776\ntest('Redirecting with a body does not cancel the current request - #1776', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/redirect') {\n      res.statusCode = 301\n      res.setHeader('location', '/redirect/')\n      res.write('<a href=\"/redirect/\">Moved Permanently</a>')\n      setTimeout(() => res.end(), 500)\n      return\n    }\n\n    res.write(req.url)\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const resp = await fetch(`http://localhost:${server.address().port}/redirect`)\n  t.assert.strictEqual(await resp.text(), '/redirect/')\n  t.assert.ok(resp.redirected)\n})\n\ntest('Redirecting with an empty body does not throw an error - #2027', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/redirect') {\n      res.statusCode = 307\n      res.setHeader('location', '/redirect/')\n      res.write('<a href=\"/redirect/\">Moved Permanently</a>')\n      res.end()\n      return\n    }\n    res.write(req.url)\n    res.end()\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const resp = await fetch(`http://localhost:${server.address().port}/redirect`, { method: 'PUT', body: '' })\n  t.assert.strictEqual(await resp.text(), '/redirect/')\n  t.assert.ok(resp.redirected)\n})\n\ntest('Redirecting with a body does not fail to write body - #2543', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/redirect') {\n      res.writeHead(307, { location: '/target' })\n      res.write('<a href=\"/redirect/\">Moved Permanently</a>')\n      setTimeout(() => res.end(), 500)\n    } else {\n      let body = ''\n      req.on('data', (chunk) => { body += chunk })\n      req.on('end', () => t.assert.strictEqual(body, 'body'))\n      res.write('ok')\n      res.end()\n    }\n  }).listen(0)\n\n  t.after(closeServerAsPromise(server))\n  await once(server, 'listening')\n\n  const resp = await fetch(`http://localhost:${server.address().port}/redirect`, {\n    method: 'POST',\n    body: 'body'\n  })\n  t.assert.strictEqual(await resp.text(), 'ok')\n  t.assert.ok(resp.redirected)\n})\n"
  },
  {
    "path": "test/fetch/referrrer-policy.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { describe, test } = require('node:test')\nconst { fetch } = require('../..')\n\ndescribe('referrer-policy', () => {\n  ;[\n    [\n      'should ignore empty string as policy',\n      'origin, asdas, asdaw34, no-referrer,,',\n      'no-referrer'\n    ],\n    [\n      'should set referrer policy from response headers on redirect',\n      'origin',\n      'origin'\n    ],\n    [\n      'should select the first valid police',\n      'asdas, origin',\n      'origin'\n    ],\n    [\n      'should select the first valid policy #2',\n      'no-referrer, asdas, origin, 0943sd',\n      'origin'\n    ],\n    [\n      'should pick the last fallback over invalid policy tokens',\n      'origin, asdas, asdaw34',\n      'origin'\n    ],\n    [\n      'should set not change request referrer policy if no Referrer-Policy from initial redirect response',\n      null,\n      'strict-origin-when-cross-origin'\n    ],\n    [\n      'should set not change request referrer policy if the policy is a non-valid Referrer Policy',\n      'asdasd',\n      'strict-origin-when-cross-origin'\n    ],\n    [\n      'should set not change request referrer policy if the policy is a non-valid Referrer Policy #2',\n      'asdasd, asdasa, 12daw,',\n      'strict-origin-when-cross-origin'\n    ],\n\n    [\n      'referrer policy is origin',\n      'origin',\n      'origin'\n    ],\n    [\n      'referrer policy is no-referrer',\n      'no-referrer',\n      'no-referrer'\n    ],\n    [\n      'referrer policy is strict-origin-when-cross-origin',\n      'strict-origin-when-cross-origin',\n      'strict-origin-when-cross-origin'\n    ],\n    [\n      'referrer policy is unsafe-url',\n      'unsafe-url',\n      'unsafe-url'\n    ]\n  ].forEach(([title, responseReferrerPolicy, expectedReferrerPolicy, referrer]) => {\n    test(title, async (t) => {\n      t.plan(1)\n\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        switch (res.req.url) {\n          case '/redirect':\n            res.writeHead(302, undefined, {\n              Location: '/target',\n              'referrer-policy': responseReferrerPolicy\n            })\n            res.end()\n            break\n          case '/target':\n            switch (expectedReferrerPolicy) {\n              case 'no-referrer':\n                t.assert.strictEqual(req.headers['referer'], undefined)\n                break\n              case 'origin':\n                t.assert.strictEqual(req.headers['referer'], `http://127.0.0.1:${port}/`)\n                break\n              case 'strict-origin-when-cross-origin':\n                t.assert.strictEqual(req.headers['referer'], `http://127.0.0.1:${port}/index.html?test=1`)\n                break\n              case 'unsafe-url':\n                t.assert.strictEqual(req.headers['referer'], `http://127.0.0.1:${port}/index.html?test=1`)\n                break\n            }\n            res.writeHead(200, 'dummy', { 'Content-Type': 'text/plain' })\n            res.end()\n            break\n        }\n      })\n\n      server.listen(0)\n      await once(server, 'listening')\n\n      const { port } = server.address()\n      await fetch(`http://127.0.0.1:${port}/redirect`, {\n        referrer: referrer || `http://127.0.0.1:${port}/index.html?test=1`\n      })\n\n      server.closeAllConnections()\n      server.closeIdleConnections()\n      server.close()\n      await once(server, 'close')\n    })\n  })\n})\n"
  },
  {
    "path": "test/fetch/relative-url.js",
    "content": "'use strict'\n\nconst { test, afterEach } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst {\n  getGlobalOrigin,\n  setGlobalOrigin,\n  Response,\n  Request,\n  fetch\n} = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nafterEach(() => setGlobalOrigin(undefined))\n\ntest('setGlobalOrigin & getGlobalOrigin', (t) => {\n  t.assert.strictEqual(getGlobalOrigin(), undefined)\n\n  setGlobalOrigin('http://localhost:3000')\n  t.assert.deepStrictEqual(getGlobalOrigin(), new URL('http://localhost:3000'))\n\n  setGlobalOrigin(undefined)\n  t.assert.strictEqual(getGlobalOrigin(), undefined)\n\n  setGlobalOrigin(new URL('http://localhost:3000'))\n  t.assert.deepStrictEqual(getGlobalOrigin(), new URL('http://localhost:3000'))\n\n  t.assert.throws(() => {\n    setGlobalOrigin('invalid.url')\n  }, TypeError)\n\n  t.assert.throws(() => {\n    setGlobalOrigin('wss://invalid.protocol')\n  }, TypeError)\n\n  t.assert.throws(() => setGlobalOrigin(true))\n})\n\ntest('Response.redirect', (t) => {\n  t.assert.throws(() => {\n    Response.redirect('/relative/path', 302)\n  }, TypeError('Failed to parse URL from /relative/path'))\n\n  t.assert.doesNotThrow(() => {\n    setGlobalOrigin('http://localhost:3000')\n    Response.redirect('/relative/path', 302)\n  })\n\n  setGlobalOrigin('http://localhost:3000')\n  const response = Response.redirect('/relative/path', 302)\n  // See step #7 of https://fetch.spec.whatwg.org/#dom-response-redirect\n  t.assert.strictEqual(response.headers.get('location'), 'http://localhost:3000/relative/path')\n})\n\ntest('new Request', (t) => {\n  t.assert.throws(\n    () => new Request('/relative/path'),\n    TypeError('Failed to parse URL from /relative/path')\n  )\n\n  t.assert.doesNotThrow(() => {\n    setGlobalOrigin('http://localhost:3000')\n    // eslint-disable-next-line no-new\n    new Request('/relative/path')\n  })\n\n  setGlobalOrigin('http://localhost:3000')\n  const request = new Request('/relative/path')\n  t.assert.strictEqual(request.url, 'http://localhost:3000/relative/path')\n})\n\ntest('fetch', async (t) => {\n  await t.assert.rejects(fetch('/relative/path'), TypeError('Failed to parse URL from /relative/path'))\n\n  await t.test('Basic fetch', async (t) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.url, '/relative/path')\n      res.end()\n    }).listen(0)\n\n    setGlobalOrigin(`http://localhost:${server.address().port}`)\n    t.after(closeServerAsPromise(server))\n    await once(server, 'listening')\n\n    await t.assert.doesNotReject(fetch('/relative/path'))\n  })\n\n  await t.test('fetch return', async (t) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.url, '/relative/path')\n      res.end()\n    }).listen(0)\n\n    setGlobalOrigin(`http://localhost:${server.address().port}`)\n    t.after(closeServerAsPromise(server))\n    await once(server, 'listening')\n\n    const response = await fetch('/relative/path')\n\n    t.assert.strictEqual(response.url, `http://localhost:${server.address().port}/relative/path`)\n  })\n})\n"
  },
  {
    "path": "test/fetch/request-inspect-custom.js",
    "content": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst util = require('node:util')\nconst { Request } = require('../../')\n\ndescribe('Request custom inspection', () => {\n  it('should return a custom inspect output', (t) => {\n    const request = new Request('https://example.com/api', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json'\n      }\n    })\n\n    const inspectedOutput = util.inspect(request)\n\n    const expectedOutput = \"Request {\\n  method: 'POST',\\n  url: 'https://example.com/api',\\n  headers: Headers { 'Content-Type': 'application/json' },\\n  destination: '',\\n  referrer: 'about:client',\\n  referrerPolicy: '',\\n  mode: 'cors',\\n  credentials: 'same-origin',\\n  cache: 'default',\\n  redirect: 'follow',\\n  integrity: '',\\n  keepalive: false,\\n  isReloadNavigation: false,\\n  isHistoryNavigation: false,\\n  signal: AbortSignal { aborted: false }\\n}\"\n    t.assert.strictEqual(inspectedOutput, expectedOutput)\n  })\n})\n"
  },
  {
    "path": "test/fetch/request.js",
    "content": "/* globals AbortController */\n\n'use strict'\n\nconst { test } = require('node:test')\nconst {\n  Request,\n  Headers,\n  fetch\n} = require('../../')\n\ntest('arg validation', async (t) => {\n  // constructor\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request()\n  }, TypeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', 0)\n  }, TypeError)\n  t.assert.throws(() => {\n    const url = new URL('http://asd')\n    url.password = 'asd'\n    // eslint-disable-next-line\n    new Request(url)\n  }, TypeError)\n  t.assert.throws(() => {\n    const url = new URL('http://asd')\n    url.username = 'asd'\n    // eslint-disable-next-line\n    new Request(url)\n  }, TypeError)\n  t.assert.doesNotThrow(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', undefined)\n  }, TypeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      window: {}\n    })\n  }, TypeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      window: 1\n    })\n  }, TypeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      mode: 'navigate'\n    })\n  })\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      referrerPolicy: 'agjhagna'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      mode: 'agjhagna'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      credentials: 'agjhagna'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      cache: 'agjhagna'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      method: 'agjhagnaöööö'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Request('http://asd', {\n      method: 'TRACE'\n    })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.destination.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.referrer.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.referrerPolicy.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.mode.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.credentials.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.cache.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.redirect.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.integrity.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.keepalive.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.isReloadNavigation.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.isHistoryNavigation.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.signal.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line no-unused-expressions\n    Request.prototype.body\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line no-unused-expressions\n    Request.prototype.bodyUsed\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Request.prototype.clone.call(null)\n  }, TypeError)\n\n  t.assert.doesNotThrow(() => {\n    Request.prototype[Symbol.toStringTag].charAt(0)\n  })\n\n  for (const method of [\n    'text',\n    'json',\n    'arrayBuffer',\n    'blob',\n    'formData'\n  ]) {\n    await t.assert.rejects(async () => {\n      await new Request('http://localhost')[method].call({\n        blob () {\n          return {\n            text () {\n              return Promise.resolve('emulating this')\n            }\n          }\n        }\n      })\n    }, TypeError)\n  }\n})\n\ntest('undefined window', (t) => {\n  t.assert.doesNotThrow(() => new Request('http://asd', { window: undefined }))\n})\n\ntest('undefined body', (t) => {\n  const req = new Request('http://asd', { body: undefined })\n  t.assert.strictEqual(req.body, null)\n})\n\ntest('undefined method', (t) => {\n  const req = new Request('http://asd', { method: undefined })\n  t.assert.strictEqual(req.method, 'GET')\n})\n\ntest('undefined headers', (t) => {\n  const req = new Request('http://asd', { headers: undefined })\n  t.assert.strictEqual([...req.headers.entries()].length, 0)\n})\n\ntest('undefined referrer', (t) => {\n  const req = new Request('http://asd', { referrer: undefined })\n  t.assert.strictEqual(req.referrer, 'about:client')\n})\n\ntest('undefined referrerPolicy', (t) => {\n  const req = new Request('http://asd', { referrerPolicy: undefined })\n  t.assert.strictEqual(req.referrerPolicy, '')\n})\n\ntest('undefined mode', (t) => {\n  const req = new Request('http://asd', { mode: undefined })\n  t.assert.strictEqual(req.mode, 'cors')\n})\n\ntest('undefined credentials', (t) => {\n  const req = new Request('http://asd', { credentials: undefined })\n  t.assert.strictEqual(req.credentials, 'same-origin')\n})\n\ntest('undefined cache', (t) => {\n  const req = new Request('http://asd', { cache: undefined })\n  t.assert.strictEqual(req.cache, 'default')\n})\n\ntest('undefined redirect', (t) => {\n  const req = new Request('http://asd', { redirect: undefined })\n  t.assert.strictEqual(req.redirect, 'follow')\n})\n\ntest('undefined keepalive', (t) => {\n  const req = new Request('http://asd', { keepalive: undefined })\n  t.assert.strictEqual(req.keepalive, false)\n})\n\ntest('undefined integrity', (t) => {\n  const req = new Request('http://asd', { integrity: undefined })\n  t.assert.strictEqual(req.integrity, '')\n})\n\ntest('null integrity', (t) => {\n  const req = new Request('http://asd', { integrity: null })\n  t.assert.strictEqual(req.integrity, 'null')\n})\n\ntest('undefined signal', (t) => {\n  const req = new Request('http://asd', { signal: undefined })\n  t.assert.strictEqual(req.signal.aborted, false)\n})\n\ntest('pre aborted signal', (t) => {\n  const ac = new AbortController()\n  ac.abort('gwak')\n  const req = new Request('http://asd', { signal: ac.signal })\n  t.assert.strictEqual(req.signal.aborted, true)\n  t.assert.strictEqual(req.signal.reason, 'gwak')\n})\n\ntest('post aborted signal', (t) => {\n  t.plan(2)\n\n  const ac = new AbortController()\n  const req = new Request('http://asd', { signal: ac.signal })\n  t.assert.strictEqual(req.signal.aborted, false)\n  ac.signal.addEventListener('abort', () => {\n    t.assert.strictEqual(req.signal.reason, 'gwak')\n  }, { once: true })\n  ac.abort('gwak')\n})\n\ntest('pre aborted signal cloned', (t) => {\n  const ac = new AbortController()\n  ac.abort('gwak')\n  const req = new Request('http://asd', { signal: ac.signal }).clone()\n  t.assert.strictEqual(req.signal.aborted, true)\n  t.assert.strictEqual(req.signal.reason, 'gwak')\n})\n\ntest('URLSearchParams body with Headers object - issue #1407', async (t) => {\n  const body = new URLSearchParams({\n    abc: 123\n  })\n\n  const request = new Request(\n    'http://localhost',\n    {\n      method: 'POST',\n      body,\n      headers: {\n        Authorization: 'test'\n      }\n    }\n  )\n\n  t.assert.strictEqual(request.headers.get('content-type'), 'application/x-www-form-urlencoded;charset=UTF-8')\n  t.assert.strictEqual(request.headers.get('authorization'), 'test')\n  t.assert.strictEqual(await request.text(), 'abc=123')\n})\n\ntest('post aborted signal cloned', (t) => {\n  t.plan(2)\n\n  const ac = new AbortController()\n  const req = new Request('http://asd', { signal: ac.signal }).clone()\n  t.assert.strictEqual(req.signal.aborted, false)\n  ac.signal.addEventListener('abort', () => {\n    t.assert.strictEqual(req.signal.reason, 'gwak')\n  }, { once: true })\n  ac.abort('gwak')\n})\n\ntest('Passing headers in init', async (t) => {\n  // https://github.com/nodejs/undici/issues/1400\n  await t.test('Headers instance', (t) => {\n    const req = new Request('http://localhost', {\n      headers: new Headers({ key: 'value' })\n    })\n\n    t.assert.strictEqual(req.headers.get('key'), 'value')\n  })\n\n  await t.test('key:value object', (t) => {\n    const req = new Request('http://localhost', {\n      headers: { key: 'value' }\n    })\n\n    t.assert.strictEqual(req.headers.get('key'), 'value')\n  })\n\n  await t.test('[key, value][]', (t) => {\n    const req = new Request('http://localhost', {\n      headers: [['key', 'value']]\n    })\n\n    t.assert.strictEqual(req.headers.get('key'), 'value')\n  })\n})\n\ntest('Symbol.toStringTag', (t) => {\n  const req = new Request('http://localhost')\n\n  t.assert.strictEqual(req[Symbol.toStringTag], 'Request')\n  t.assert.strictEqual(Request.prototype[Symbol.toStringTag], 'Request')\n})\n\ntest('invalid RequestInit values', (t) => {\n  /* eslint-disable no-new */\n  t.assert.throws(() => {\n    new Request('http://l', { mode: 'CoRs' })\n  }, TypeError, 'not exact case = error')\n\n  t.assert.throws(() => {\n    new Request('http://l', { mode: 'random' })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    new Request('http://l', { credentials: 'OMIt' })\n  }, TypeError, 'not exact case = error')\n\n  t.assert.throws(() => {\n    new Request('http://l', { credentials: 'random' })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    new Request('http://l', { cache: 'DeFaULt' })\n  }, TypeError, 'not exact case = error')\n\n  t.assert.throws(() => {\n    new Request('http://l', { cache: 'random' })\n  }, TypeError)\n\n  t.assert.throws(() => {\n    new Request('http://l', { redirect: 'FOllOW' })\n  }, TypeError, 'not exact case = error')\n\n  t.assert.throws(() => {\n    new Request('http://l', { redirect: 'random' })\n  }, TypeError)\n  /* eslint-enable no-new */\n})\n\ntest('RequestInit.signal option', async (t) => {\n  t.assert.throws(() => {\n    // eslint-disable-next-line no-new\n    new Request('http://asd', {\n      signal: true\n    })\n  }, TypeError)\n\n  await t.assert.rejects(fetch('http://asd', {\n    signal: false\n  }), TypeError)\n})\n\n// https://github.com/nodejs/undici/issues/2050\ntest('set-cookie headers get cleared when passing a Request as first param', (t) => {\n  const req1 = new Request('http://localhost', {\n    headers: {\n      'set-cookie': 'a=1'\n    }\n  })\n\n  t.assert.deepStrictEqual([...req1.headers], [['set-cookie', 'a=1']])\n  const req2 = new Request(req1, { headers: {} })\n  t.assert.deepStrictEqual([...req1.headers], [['set-cookie', 'a=1']])\n  t.assert.deepStrictEqual([...req2.headers], [])\n  t.assert.deepStrictEqual(req2.headers.getSetCookie(), [])\n})\n\n// https://github.com/nodejs/undici/issues/2124\ntest('request.referrer', (t) => {\n  for (const referrer of ['about://client', 'about://client:1234']) {\n    const request = new Request('http://a', { referrer })\n\n    t.assert.strictEqual(request.referrer, 'about:client')\n  }\n})\n\n// https://github.com/nodejs/undici/issues/2445\ntest('Clone the set-cookie header when Request is passed as the first parameter and no header is passed.', (t) => {\n  const request = new Request('http://localhost', { headers: { 'set-cookie': 'A' } })\n  const request2 = new Request(request)\n  t.assert.deepStrictEqual([...request.headers], [['set-cookie', 'A']])\n  request2.headers.append('set-cookie', 'B')\n  t.assert.deepStrictEqual([...request.headers], [['set-cookie', 'A']])\n  t.assert.strictEqual(request.headers.getSetCookie().join(', '), request.headers.get('set-cookie'))\n  t.assert.strictEqual(request2.headers.getSetCookie().join(', '), request2.headers.get('set-cookie'))\n})\n\n// Tests for optimization introduced in https://github.com/nodejs/undici/pull/2456\ntest('keys to object prototypes method', (t) => {\n  const request = new Request('http://localhost', { method: 'hasOwnProperty' })\n  t.assert.ok(typeof request.method === 'string')\n})\n\n// https://github.com/nodejs/undici/issues/2465\ntest('Issue#2465', async (t) => {\n  t.plan(1)\n  const request = new Request('http://localhost', { body: new SharedArrayBuffer(0), method: 'POST' })\n  t.assert.strictEqual(await request.text(), '[object SharedArrayBuffer]')\n})\n"
  },
  {
    "path": "test/fetch/resource-timing.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { fetch, Agent } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst {\n  PerformanceObserver,\n  performance\n} = require('node:perf_hooks')\n\ntest('should create a PerformanceResourceTiming after each fetch request', (t, done) => {\n  t.plan(8)\n\n  const obs = new PerformanceObserver(list => {\n    const expectedResourceEntryName = `http://localhost:${server.address().port}/`\n\n    const entries = list.getEntries()\n    t.assert.strictEqual(entries.length, 1)\n    const [entry] = entries\n    t.assert.strictEqual(entry.name, expectedResourceEntryName)\n    t.assert.strictEqual(entry.entryType, 'resource')\n\n    t.assert.ok(entry.duration >= 0)\n    t.assert.ok(entry.startTime >= 0)\n\n    const entriesByName = list.getEntriesByName(expectedResourceEntryName)\n    t.assert.strictEqual(entriesByName.length, 1)\n    t.assert.deepStrictEqual(entriesByName[0], entry)\n\n    obs.disconnect()\n    performance.clearResourceTimings()\n    done()\n  })\n\n  obs.observe({ entryTypes: ['resource'] })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ok')\n  }).listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.strictEqual('ok', await body.text())\n  })\n\n  t.after(closeServerAsPromise(server))\n})\n\ntest('should include encodedBodySize in performance entry', (t, done) => {\n  t.plan(4)\n  const obs = new PerformanceObserver(list => {\n    const [entry] = list.getEntries()\n    t.assert.strictEqual(entry.encodedBodySize, 2)\n    t.assert.strictEqual(entry.decodedBodySize, 2)\n    t.assert.strictEqual(entry.transferSize, 2 + 300)\n\n    obs.disconnect()\n    performance.clearResourceTimings()\n    done()\n  })\n\n  obs.observe({ entryTypes: ['resource'] })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ok')\n  }).listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`)\n    t.assert.strictEqual('ok', await body.text())\n  })\n\n  t.after(closeServerAsPromise(server))\n})\n\ntest('timing entries should be in order', (t, done) => {\n  t.plan(13)\n  const obs = new PerformanceObserver(list => {\n    const [entry] = list.getEntries()\n\n    t.assert.ok(entry.startTime > 0)\n    t.assert.ok(entry.fetchStart >= entry.startTime)\n    t.assert.ok(entry.domainLookupStart >= entry.fetchStart)\n    t.assert.ok(entry.domainLookupEnd >= entry.domainLookupStart)\n    t.assert.ok(entry.connectStart >= entry.domainLookupEnd)\n    t.assert.ok(entry.connectEnd >= entry.connectStart)\n    t.assert.ok(entry.requestStart >= entry.connectEnd)\n    t.assert.ok(entry.responseStart >= entry.requestStart)\n    t.assert.ok(entry.responseEnd >= entry.responseStart)\n    t.assert.ok(entry.duration > 0)\n\n    t.assert.ok(entry.redirectStart === 0)\n    t.assert.ok(entry.redirectEnd === 0)\n\n    obs.disconnect()\n    performance.clearResourceTimings()\n    done()\n  })\n\n  obs.observe({ entryTypes: ['resource'] })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ok')\n  }).listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}/redirect`)\n    t.assert.strictEqual('ok', await body.text())\n  })\n\n  t.after(closeServerAsPromise(server))\n})\n\ntest('redirect timing entries should be included when redirecting', (t, done) => {\n  t.plan(4)\n  const obs = new PerformanceObserver(list => {\n    const [entry] = list.getEntries()\n\n    t.assert.ok(entry.redirectStart >= entry.startTime)\n    t.assert.ok(entry.redirectEnd >= entry.redirectStart)\n    t.assert.ok(entry.connectStart >= entry.redirectEnd)\n\n    obs.disconnect()\n    performance.clearResourceTimings()\n    done()\n  })\n\n  obs.observe({ entryTypes: ['resource'] })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/redirect') {\n      res.statusCode = 307\n      res.setHeader('location', '/redirect/')\n      res.end()\n      return\n    }\n    res.end('ok')\n  }).listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}/redirect`)\n    t.assert.strictEqual('ok', await body.text())\n  })\n\n  t.after(closeServerAsPromise(server))\n})\n\ntest('responseStart should be greater than 0 with composed interceptor', (t, done) => {\n  t.plan(4)\n  const obs = new PerformanceObserver(list => {\n    const [entry] = list.getEntries()\n\n    t.assert.ok(entry.requestStart > 0, `requestStart should be > 0, got ${entry.requestStart}`)\n    t.assert.ok(entry.responseStart > 0, `responseStart should be > 0, got ${entry.responseStart}`)\n    t.assert.ok(entry.responseStart >= entry.requestStart, 'responseStart should be >= requestStart')\n\n    obs.disconnect()\n    performance.clearResourceTimings()\n    done()\n  })\n\n  obs.observe({ entryTypes: ['resource'] })\n\n  const dispatcher = new Agent().compose((dispatch) => (opts, handler) => dispatch(opts, handler))\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ok')\n  }).listen(0, async () => {\n    const body = await fetch(`http://localhost:${server.address().port}`, { dispatcher })\n    t.assert.strictEqual('ok', await body.text())\n  })\n\n  t.after(closeServerAsPromise(server))\n})\n"
  },
  {
    "path": "test/fetch/response-inspect-custom.js",
    "content": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst util = require('node:util')\nconst { Response } = require('../../')\n\ndescribe('Response custom inspection', () => {\n  it('should return a custom inspect output', (t) => {\n    const response = new Response(null)\n    const inspectedOutput = util.inspect(response, {\n      depth: null,\n      getters: true\n    })\n\n    const expectedOutput = `Response {\n  status: 200,\n  statusText: '',\n  headers: Headers {},\n  body: null,\n  bodyUsed: false,\n  ok: true,\n  redirected: false,\n  type: 'default',\n  url: ''\n}`\n\n    t.assert.strictEqual(inspectedOutput, expectedOutput)\n  })\n})\n"
  },
  {
    "path": "test/fetch/response-json.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Response } = require('../../')\n\n// https://github.com/web-platform-tests/wpt/pull/32825/\n\nconst APPLICATION_JSON = 'application/json'\nconst FOO_BAR = 'foo/bar'\n\nconst INIT_TESTS = [\n  [undefined, 200, '', APPLICATION_JSON, {}],\n  [{ status: 400 }, 400, '', APPLICATION_JSON, {}],\n  [{ statusText: 'foo' }, 200, 'foo', APPLICATION_JSON, {}],\n  [{ headers: {} }, 200, '', APPLICATION_JSON, {}],\n  [{ headers: { 'content-type': FOO_BAR } }, 200, '', FOO_BAR, {}],\n  [{ headers: { 'x-foo': 'bar' } }, 200, '', APPLICATION_JSON, { 'x-foo': 'bar' }]\n]\n\ntest('Check response returned by static json() with init', async (t) => {\n  for (const [init, expectedStatus, expectedStatusText, expectedContentType, expectedHeaders] of INIT_TESTS) {\n    const response = Response.json('hello world', init)\n    t.assert.strictEqual(response.type, 'default', \"Response's type is default\")\n    t.assert.strictEqual(response.status, expectedStatus, \"Response's status is \" + expectedStatus)\n    t.assert.strictEqual(response.statusText, expectedStatusText, \"Response's statusText is \" + JSON.stringify(expectedStatusText))\n    t.assert.strictEqual(response.headers.get('content-type'), expectedContentType, \"Response's content-type is \" + expectedContentType)\n    for (const key in expectedHeaders) {\n      t.assert.strictEqual(response.headers.get(key), expectedHeaders[key], \"Response's header \" + key + ' is ' + JSON.stringify(expectedHeaders[key]))\n    }\n\n    const data = await response.json()\n    t.assert.strictEqual(data, 'hello world', \"Response's body is 'hello world'\")\n  }\n})\n\ntest('Throws TypeError when calling static json() with an invalid status', (t) => {\n  const nullBodyStatus = [204, 205, 304]\n\n  for (const status of nullBodyStatus) {\n    t.assert.throws(() => {\n      Response.json('hello world', { status })\n    }, TypeError, `Throws TypeError when calling static json() with a status of ${status}`)\n  }\n})\n\ntest('Check static json() encodes JSON objects correctly', async (t) => {\n  const response = Response.json({ foo: 'bar' })\n  const data = await response.json()\n  t.assert.strictEqual(typeof data, 'object', \"Response's json body is an object\")\n  t.assert.strictEqual(data.foo, 'bar', \"Response's json body is { foo: 'bar' }\")\n})\n\ntest('Check static json() throws when data is not encodable', (t) => {\n  t.assert.throws(() => {\n    Response.json(Symbol('foo'))\n  }, TypeError)\n})\n\ntest('Check static json() throws when data is circular', (t) => {\n  const a = { b: 1 }\n  a.a = a\n\n  t.assert.throws(() => {\n    Response.json(a)\n  }, TypeError)\n})\n\ntest('Check static json() propagates JSON serializer errors', (t) => {\n  class CustomError extends Error {\n    name = 'CustomError'\n  }\n\n  t.assert.throws(() => {\n    Response.json({ get foo () { throw new CustomError('bar') } })\n  }, CustomError)\n})\n\n// note: these tests are not part of any WPTs\ntest('unserializable values', (t) => {\n  t.assert.throws(() => {\n    Response.json(Symbol('symbol'))\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.json(undefined)\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.json()\n  }, TypeError)\n})\n\ntest('invalid init', (t) => {\n  t.assert.throws(() => {\n    Response.json(null, 3)\n  }, TypeError)\n})\n"
  },
  {
    "path": "test/fetch/response.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst { setImmediate } = require('node:timers/promises')\nconst { AsyncLocalStorage } = require('node:async_hooks')\nconst {\n  Response,\n  FormData\n} = require('../../')\n\ntest('arg validation', async (t) => {\n  // constructor\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Response(null, 0)\n  }, TypeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Response(null, {\n      status: 99\n    })\n  }, RangeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Response(null, {\n      status: 600\n    })\n  }, RangeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Response(null, {\n      status: '600'\n    })\n  }, RangeError)\n  t.assert.throws(() => {\n    // eslint-disable-next-line\n    new Response(null, {\n      statusText: '\\u0000'\n    })\n  }, TypeError)\n\n  for (const nullStatus of [204, 205, 304]) {\n    t.assert.throws(() => {\n      // eslint-disable-next-line\n      new Response(new ArrayBuffer(16), {\n        status: nullStatus\n      })\n    }, TypeError)\n  }\n\n  t.assert.doesNotThrow(() => {\n    Response.prototype[Symbol.toStringTag].charAt(0)\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.type.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.url.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.redirected.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.status.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.ok.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.statusText.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.headers.toString()\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line no-unused-expressions\n    Response.prototype.body\n  }, TypeError)\n\n  t.assert.throws(() => {\n    // eslint-disable-next-line no-unused-expressions\n    Response.prototype.bodyUsed\n  }, TypeError)\n\n  t.assert.throws(() => {\n    Response.prototype.clone.call(null)\n  }, TypeError)\n\n  await t.assert.rejects(\n    new Response('http://localhost').text.call({\n      blob () {\n        return {\n          text () {\n            return Promise.resolve('emulating response.blob()')\n          }\n        }\n      }\n    }), TypeError)\n})\n\ntest('response clone', (t) => {\n  // https://github.com/nodejs/undici/issues/1122\n  const response1 = new Response(null, { status: 201 })\n  const response2 = new Response(undefined, { status: 201 })\n\n  t.assert.deepStrictEqual(response1.body, response1.clone().body)\n  t.assert.deepStrictEqual(response2.body, response2.clone().body)\n  t.assert.strictEqual(response2.body, null)\n})\n\ntest('Symbol.toStringTag', (t) => {\n  const resp = new Response()\n\n  t.assert.strictEqual(resp[Symbol.toStringTag], 'Response')\n  t.assert.strictEqual(Response.prototype[Symbol.toStringTag], 'Response')\n})\n\ntest('async iterable body', async (t) => {\n  const asyncIterable = {\n    async * [Symbol.asyncIterator] () {\n      yield 'a'\n      yield 'b'\n      yield 'c'\n    }\n  }\n\n  const response = new Response(asyncIterable)\n  t.assert.strictEqual(await response.text(), 'abc')\n})\n\n// https://github.com/nodejs/node/pull/43752#issuecomment-1179678544\ntest('Modifying headers using Headers.prototype.set', (t) => {\n  const response = new Response('body', {\n    headers: {\n      'content-type': 'test/test',\n      'Content-Encoding': 'hello/world'\n    }\n  })\n\n  const response2 = response.clone()\n\n  response.headers.set('content-type', 'application/wasm')\n  response.headers.set('Content-Encoding', 'world/hello')\n\n  t.assert.strictEqual(response.headers.get('content-type'), 'application/wasm')\n  t.assert.strictEqual(response.headers.get('Content-Encoding'), 'world/hello')\n\n  response2.headers.delete('content-type')\n  response2.headers.delete('Content-Encoding')\n\n  t.assert.strictEqual(response2.headers.get('content-type'), null)\n  t.assert.strictEqual(response2.headers.get('Content-Encoding'), null)\n})\n\n// https://github.com/nodejs/node/issues/43838\ndescribe('constructing a Response with a ReadableStream body', () => {\n  const text = '{\"foo\":\"bar\"}'\n  const uint8 = new TextEncoder().encode(text)\n\n  test('Readable stream with Uint8Array chunks', async (t) => {\n    const readable = new ReadableStream({\n      start (controller) {\n        controller.enqueue(uint8)\n        controller.close()\n      }\n    })\n\n    const response1 = new Response(readable)\n    const response2 = response1.clone()\n    const response3 = response1.clone()\n\n    t.assert.strictEqual(await response1.text(), text)\n    t.assert.deepStrictEqual(await response2.arrayBuffer(), uint8.buffer)\n    t.assert.deepStrictEqual(await response3.json(), JSON.parse(text))\n  })\n\n  test('.arrayBuffer() correctly clones multiple buffers', async (t) => {\n    const buffer = Buffer.allocUnsafeSlow(2 * 1024 - 2)\n    const readable = new ReadableStream({\n      start (controller) {\n        for (let i = 0; i < buffer.length; i += 128) {\n          controller.enqueue(buffer.slice(i, i + 128))\n        }\n        controller.close()\n      }\n    })\n\n    const response = new Response(readable)\n    t.assert.deepStrictEqual(await response.arrayBuffer(), buffer.buffer)\n  })\n\n  test('Readable stream with non-Uint8Array chunks', async (t) => {\n    const readable = new ReadableStream({\n      start (controller) {\n        controller.enqueue(text) // string\n        controller.close()\n      }\n    })\n\n    const response = new Response(readable)\n\n    await t.assert.rejects(response.text(), TypeError)\n  })\n\n  test('Readable with ArrayBuffer chunk still throws', async (t) => {\n    const readable = new ReadableStream({\n      start (controller) {\n        controller.enqueue(uint8.buffer)\n        controller.close()\n      }\n    })\n\n    const response1 = new Response(readable)\n    const response2 = response1.clone()\n    const response3 = response1.clone()\n    const response4 = response1.clone()\n\n    await t.assert.rejects(response1.arrayBuffer(), TypeError)\n    await t.assert.rejects(response2.text(), TypeError)\n    await t.assert.rejects(response3.json(), TypeError)\n    await t.assert.rejects(response4.blob(), TypeError)\n  })\n})\n\n// https://github.com/nodejs/undici/issues/2465\ntest('Issue#2465', async (t) => {\n  t.plan(1)\n  const response = new Response(new SharedArrayBuffer(0))\n  t.assert.strictEqual(await response.text(), '[object SharedArrayBuffer]')\n})\n\ndescribe('Check the Content-Type of invalid formData', () => {\n  test('_application/x-www-form-urlencoded', async (t) => {\n    t.plan(1)\n    const response = new Response('x=y', { headers: { 'content-type': '_application/x-www-form-urlencoded' } })\n    await t.assert.rejects(response.formData(), TypeError)\n  })\n\n  test('_multipart/form-data', async (t) => {\n    t.plan(1)\n    const formData = new FormData()\n    formData.append('x', 'y')\n    const response = new Response(formData, { headers: { 'content-type': '_multipart/form-data' } })\n    await t.assert.rejects(response.formData(), TypeError)\n  })\n\n  test('application/x-www-form-urlencoded_', async (t) => {\n    t.plan(1)\n    const response = new Response('x=y', { headers: { 'content-type': 'application/x-www-form-urlencoded_' } })\n    await t.assert.rejects(response.formData(), TypeError)\n  })\n\n  test('multipart/form-data_', async (t) => {\n    t.plan(1)\n    const formData = new FormData()\n    formData.append('x', 'y')\n    const response = new Response(formData, { headers: { 'content-type': 'multipart/form-data_' } })\n    await t.assert.rejects(response.formData(), TypeError)\n  })\n})\n\ntest('clone body garbage collection', async (t) => {\n  if (typeof global.gc === 'undefined') {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n  const asyncLocalStorage = new AsyncLocalStorage()\n  let ref\n\n  await new Promise(resolve => {\n    asyncLocalStorage.run(new Map(), async () => {\n      const res = new Response('hello world')\n      const clone = res.clone()\n\n      asyncLocalStorage.getStore().set('key', clone)\n      ref = new WeakRef(clone.body)\n\n      await res.text()\n      await clone.text() // consume body\n\n      resolve()\n    })\n  })\n\n  await setImmediate()\n  global.gc()\n\n  const cloneBody = ref.deref()\n  t.assert.strictEqual(cloneBody, undefined, 'clone body was not garbage collected')\n})\n"
  },
  {
    "path": "test/fetch/spread.js",
    "content": "'use strict'\n\nconst undici = require('../..')\nconst { test } = require('node:test')\nconst { inspect } = require('node:util')\n\ntest('spreading web classes yields empty objects', (t) => {\n  for (const object of [\n    new undici.FormData(),\n    new undici.Response(null),\n    new undici.Request('http://a')\n  ]) {\n    t.assert.deepStrictEqual({ ...object }, {})\n  }\n})\n\ntest('Objects only have an expected set of symbols on their prototypes', (t) => {\n  const allowedSymbols = [\n    Symbol.iterator,\n    Symbol.toStringTag,\n    inspect.custom\n  ]\n\n  for (const object of [\n    undici.FormData,\n    undici.Response,\n    undici.Request,\n    undici.Headers,\n    undici.WebSocket,\n    undici.MessageEvent,\n    undici.CloseEvent,\n    undici.ErrorEvent,\n    undici.EventSource\n  ]) {\n    const symbols = Object.keys(Object.getOwnPropertyDescriptors(object.prototype))\n      .filter(v => typeof v === 'symbol')\n\n    t.assert.ok(symbols.every(symbol => allowedSymbols.includes(symbol)))\n  }\n})\n"
  },
  {
    "path": "test/fetch/user-agent.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst events = require('node:events')\nconst http = require('node:http')\nconst undici = require('../../')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst nodeBuild = require('../../undici-fetch.js')\n\ntest('user-agent defaults correctly', async (t) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify({ userAgentHeader: req.headers['user-agent'] }))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0)\n  await events.once(server, 'listening')\n  const url = `http://localhost:${server.address().port}`\n  const [nodeBuildJSON, undiciJSON] = await Promise.all([\n    nodeBuild.fetch(url).then((body) => body.json()),\n    undici.fetch(url).then((body) => body.json())\n  ])\n\n  t.assert.strictEqual(nodeBuildJSON.userAgentHeader, 'node')\n  t.assert.strictEqual(undiciJSON.userAgentHeader, 'undici')\n})\n\ntest('set user-agent for fetch', async (t) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(JSON.stringify({ userAgentHeader: req.headers['user-agent'] }))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0)\n  await events.once(server, 'listening')\n  const url = `http://localhost:${server.address().port}`\n  const [nodeBuildJSON, undiciJSON] = await Promise.all([\n    nodeBuild.fetch(url, { headers: { 'user-agent': 'AcmeCo Crawler - acme.co - node@acme.co' } }).then((body) => body.json()),\n    undici.fetch(url, {\n      headers: { 'user-agent': 'AcmeCo Crawler - acme.co - undici@acme.co' }\n    }).then((body) => body.json())\n  ])\n\n  t.assert.strictEqual(nodeBuildJSON.userAgentHeader, 'AcmeCo Crawler - acme.co - node@acme.co')\n  t.assert.strictEqual(undiciJSON.userAgentHeader, 'AcmeCo Crawler - acme.co - undici@acme.co')\n})\n"
  },
  {
    "path": "test/fetch/util.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst util = require('../../lib/web/fetch/util')\nconst { HeadersList } = require('../../lib/web/fetch/headers')\n\ntest('responseURL', (t) => {\n  t.plan(2)\n\n  t.assert.ok(util.responseURL({\n    urlList: [\n      new URL('http://asd'),\n      new URL('http://fgh')\n    ]\n  }))\n  t.assert.ok(!util.responseURL({\n    urlList: []\n  }))\n})\n\ntest('responseLocationURL', (t) => {\n  t.plan(3)\n\n  const acceptHeaderList = new HeadersList()\n  acceptHeaderList.append('Accept', '*/*')\n\n  const locationHeaderList = new HeadersList()\n  locationHeaderList.append('Location', 'http://asd')\n\n  t.assert.ok(!util.responseLocationURL({\n    status: 200\n  }))\n  t.assert.ok(!util.responseLocationURL({\n    status: 301,\n    headersList: acceptHeaderList\n  }))\n  t.assert.ok(util.responseLocationURL({\n    status: 301,\n    headersList: locationHeaderList,\n    urlList: [\n      new URL('http://asd'),\n      new URL('http://fgh')\n    ]\n  }))\n})\n\ntest('requestBadPort', (t) => {\n  t.plan(3)\n\n  t.assert.strictEqual('allowed', util.requestBadPort({\n    urlList: [new URL('https://asd')]\n  }))\n  t.assert.strictEqual('blocked', util.requestBadPort({\n    urlList: [new URL('http://asd:7')]\n  }))\n  t.assert.strictEqual('blocked', util.requestBadPort({\n    urlList: [new URL('https://asd:7')]\n  }))\n})\n\n// https://html.spec.whatwg.org/multipage/origin.html#same-origin\n// look at examples\ntest('sameOrigin', async (t) => {\n  await t.test('first test', (t) => {\n    const A = {\n      protocol: 'https:',\n      hostname: 'example.org',\n      port: ''\n    }\n\n    const B = {\n      protocol: 'https:',\n      hostname: 'example.org',\n      port: ''\n    }\n\n    t.assert.ok(util.sameOrigin(A, B))\n  })\n\n  await t.test('second test', (t) => {\n    const A = {\n      protocol: 'https:',\n      hostname: 'example.org',\n      port: '314'\n    }\n\n    const B = {\n      protocol: 'https:',\n      hostname: 'example.org',\n      port: '420'\n    }\n\n    t.assert.ok(!util.sameOrigin(A, B))\n  })\n\n  await t.test('obviously shouldn\\'t be equal', (t) => {\n    t.assert.ok(!util.sameOrigin(\n      { protocol: 'http:', hostname: 'example.org' },\n      { protocol: 'https:', hostname: 'example.org' }\n    ))\n\n    t.assert.ok(!util.sameOrigin(\n      { protocol: 'https:', hostname: 'example.org' },\n      { protocol: 'https:', hostname: 'example.com' }\n    ))\n  })\n\n  await t.test('file:// urls', (t) => {\n    // urls with opaque origins should return true\n\n    const a = new URL('file:///C:/undici')\n    const b = new URL('file:///var/undici')\n\n    t.assert.ok(util.sameOrigin(a, b))\n  })\n})\n\ntest('isURLPotentiallyTrustworthy', (t) => {\n  // https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-let-localhost-be-localhost#section-5.2\n  const valid = [\n    'http://localhost',\n    'http://localhost.',\n    'http://127.0.0.1',\n    'http://[::1]',\n    'https://something.com',\n    'wss://hello.com',\n    'data:text/plain;base64,randomstring',\n    'about:blank',\n    'about:srcdoc',\n    'http://subdomain.localhost',\n    'http://subdomain.localhost.',\n    'http://adb.localhost',\n    'http://localhost.localhost',\n    'blob:http://example.com/550e8400-e29b-41d4-a716-446655440000'\n  ]\n  const invalid = [\n    'http://localhost.example.com',\n    'http://subdomain.localhost.example.com',\n    'file:///link/to/file.txt',\n    'http://121.3.4.5:55',\n    'null:8080',\n    'something:8080'\n  ]\n\n  t.plan(valid.length + invalid.length + 1)\n  t.assert.ok(!util.isURLPotentiallyTrustworthy('string'))\n\n  for (const url of valid) {\n    const instance = new URL(url)\n    t.assert.ok(util.isURLPotentiallyTrustworthy(instance), instance)\n  }\n\n  for (const url of invalid) {\n    const instance = new URL(url)\n    t.assert.ok(!util.isURLPotentiallyTrustworthy(instance))\n  }\n})\n\ndescribe('setRequestReferrerPolicyOnRedirect', () => {\n  [\n    [\n      'should ignore empty string as policy',\n      'origin, asdas, asdaw34, no-referrer,,',\n      'no-referrer'\n    ],\n    [\n      'should set referrer policy from response headers on redirect',\n      'origin',\n      'origin'\n    ],\n    [\n      'should select the first valid policy from a response',\n      'asdas, origin',\n      'origin'\n    ],\n    [\n      'should select the first valid policy from a response#2',\n      'no-referrer, asdas, origin, 0943sd',\n      'origin'\n    ],\n    [\n      'should pick the last fallback over invalid policy tokens',\n      'origin, asdas, asdaw34',\n      'origin'\n    ],\n    [\n      'should set not change request referrer policy if no Referrer-Policy from initial redirect response',\n      null,\n      'no-referrer, strict-origin-when-cross-origin'\n    ],\n    [\n      'should set not change request referrer policy if the policy is a non-valid Referrer Policy',\n      'asdasd',\n      'no-referrer, strict-origin-when-cross-origin'\n    ],\n    [\n      'should set not change request referrer policy if the policy is a non-valid Referrer Policy #2',\n      'asdasd, asdasa, 12daw,',\n      'no-referrer, strict-origin-when-cross-origin'\n    ]\n  ].forEach(([title, responseReferrerPolicy, expected]) => {\n    test(title, (t) => {\n      t.plan(1)\n\n      const request = {\n        referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'\n      }\n\n      const actualResponse = {\n        headersList: new HeadersList()\n      }\n\n      actualResponse.headersList.append('Connection', 'close')\n      actualResponse.headersList.append('Location', 'https://some-location.com/redirect')\n      if (responseReferrerPolicy) {\n        actualResponse.headersList.append('Referrer-Policy', responseReferrerPolicy)\n      }\n      util.setRequestReferrerPolicyOnRedirect(request, actualResponse)\n\n      t.assert.strictEqual(request.referrerPolicy, expected)\n    })\n  })\n})\n\ndescribe('urlHasHttpsScheme', () => {\n  const { urlHasHttpsScheme } = util\n\n  test('should return false for http url', (t) => {\n    t.assert.strictEqual(urlHasHttpsScheme('http://example.com'), false)\n  })\n  test('should return true for https url', (t) => {\n    t.assert.strictEqual(urlHasHttpsScheme('https://example.com'), true)\n  })\n  test('should return false for http object', (t) => {\n    t.assert.strictEqual(urlHasHttpsScheme({ protocol: 'http:' }), false)\n  })\n  test('should return true for https object', (t) => {\n    t.assert.strictEqual(urlHasHttpsScheme({ protocol: 'https:' }), true)\n  })\n})\n\ndescribe('isValidHeaderValue', () => {\n  const { isValidHeaderValue } = util\n\n  test('should return true for valid string', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('valid123'), true)\n    t.assert.strictEqual(isValidHeaderValue('va lid123'), true)\n    t.assert.strictEqual(isValidHeaderValue('va\\tlid123'), true)\n  })\n  test('should return false for string containing NUL', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('invalid\\0'), false)\n    t.assert.strictEqual(isValidHeaderValue('in\\0valid'), false)\n    t.assert.strictEqual(isValidHeaderValue('\\0invalid'), false)\n  })\n  test('should return false for string containing CR', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('invalid\\r'), false)\n    t.assert.strictEqual(isValidHeaderValue('in\\rvalid'), false)\n    t.assert.strictEqual(isValidHeaderValue('\\rinvalid'), false)\n  })\n  test('should return false for string containing LF', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('invalid\\n'), false)\n    t.assert.strictEqual(isValidHeaderValue('in\\nvalid'), false)\n    t.assert.strictEqual(isValidHeaderValue('\\ninvalid'), false)\n  })\n\n  test('should return false for string with leading TAB', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('\\tinvalid'), false)\n  })\n  test('should return false for string with trailing TAB', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('invalid\\t'), false)\n  })\n  test('should return false for string with leading SPACE', (t) => {\n    t.assert.strictEqual(isValidHeaderValue(' invalid'), false)\n  })\n  test('should return false for string with trailing SPACE', (t) => {\n    t.assert.strictEqual(isValidHeaderValue('invalid '), false)\n  })\n})\n\ndescribe('isOriginIPPotentiallyTrustworthy()', () => {\n  [\n    ['', false],\n    ['0000:0000:0000:0000:0000:0000:0000:0001', true],\n    ['0001:0000:0000:0000:0000:0000:0000:0001', false],\n    ['0000:0000:0000:0000:0000:0000::0001', true],\n    ['0001:0000:0000:0000:0000:0000::0001', false],\n    ['0000:0000:0001:0000:0000:0000::0001', false],\n    ['0000:0000:0000:0000:0000::0001', true],\n    ['0000:0000:0000:0000::0001', true],\n    ['0000:0000:0000::0001', true],\n    ['0000:0000::0001', true],\n    ['0000::0001', true],\n    ['::1001', false],\n    ['::0001', true],\n    ['::0011', false],\n    ['::1', true],\n    ['[::1]', true],\n    ['[::1', false],\n    ['::1]', false],\n    ['::2', false],\n    ['::', false],\n    ['126.0.0.0', false],\n    ['127.0.0.0', true],\n    ['127.0.0.1', true],\n    ['127.255.255.255', true],\n    ['128.255.255.255', false]\n  ].forEach(([ip, expected]) => {\n    test(`${ip} is ${expected ? '' : 'not '}potentially trustworthy`, (t) => {\n      t.assert.strictEqual(util.isOriginIPPotentiallyTrustworthy(ip), expected)\n    })\n  })\n})\n"
  },
  {
    "path": "test/fixed-queue.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\n\nconst FixedQueue = require('../lib/dispatcher/fixed-queue')\n\ntest('fixed queue 1', (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const queue = new FixedQueue()\n  t.strictEqual(queue.head, queue.tail)\n  t.ok(queue.isEmpty())\n  queue.push('a')\n  t.ok(!queue.isEmpty())\n  t.strictEqual(queue.shift(), 'a')\n  t.strictEqual(queue.shift(), null)\n})\n\ntest('fixed queue 2', (t) => {\n  t = tspl(t, { plan: 7 + 2047 })\n\n  const queue = new FixedQueue()\n  for (let i = 0; i < 2047; i++) {\n    queue.push('a')\n  }\n  t.ok(queue.head.isFull())\n  queue.push('a')\n  t.ok(!queue.head.isFull())\n\n  t.notEqual(queue.head, queue.tail)\n  for (let i = 0; i < 2047; i++) {\n    t.strictEqual(queue.shift(), 'a')\n  }\n  t.strictEqual(queue.head, queue.tail)\n  t.ok(!queue.isEmpty())\n  t.strictEqual(queue.shift(), 'a')\n  t.ok(queue.isEmpty())\n})\n"
  },
  {
    "path": "test/fixtures/ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIF1zCCA7+gAwIBAgIUCZzRXzKGblWJpjDUDX+847p1PGMwDQYJKoZIhvcNAQEL\nBQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G\nA1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe\nBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTI0MTAwMTA3NDMzNloY\nDzMwMjQwMjAyMDc0MzM2WjB6MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDDAK\nBgNVBAMMA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwggIi\nMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCbfGWGTmAiFP94HuNdDqINvAyB\nci7xsqa2OgL5sx/0mHpsJKV3DggNZreBn/DGDKqBjgkKJhZ3ZTBjrzsGfKXunj6n\nsrpOPdm8EicMT7kV4nXvD16q7j2m0QYiUhzc9+gb+9uNmO220ZJUDhKm/LNuwBfR\nlAJ7WEaAVt9o1isGhTe95iFHpLNsj4nQ79XQZGoql8WsheRYaRBsgYDsccgfCvhH\n3/H+IZN1Zn5ITq9+WmUAu17q40vc4DSrpNWhIJY/CZGgg8tIHSYx6xbAD7CaHb2N\nsJwFbCre/Mpk5gRwh83/RCBryZ8ETBysSTs+XCJbQFMgHr0RuSL0BTqSe+Kc2RaP\noMytGkosULd91nG6PIP6KXBCzICpUhqvxDMmX4HFZ6E7iqbKoOnhbWWLROFEwGm4\nmWDws2Cf20XrhVDMcusm1lZUVv707EeS7KaxbXbtut9egkdb+u8xAkhlJV877G0p\n1LYpwkKul7Rb/WtF1pMXz8kVLkiBQ8neAnIwYqycD+AWPD72yi2l25Lva1ORzdnY\n/3+iE3qq9G7D9Wymj60BzEIDfgWdQ7hbREX7AvgHb/jUwXNI3keUoMKm0y8LSVCn\nanJjttduMvKEY4LUBrQmIkJIijnXJqfnTzahssnhMli6TaBDhgKFXCtufS+OhPjK\n6gklbY03T5oG5dpvEwIDAQABo1MwUTAdBgNVHQ4EFgQUMii3SZU8I+FEmIBfkoo/\nE3rMG+cwHwYDVR0jBBgwFoAUMii3SZU8I+FEmIBfkoo/E3rMG+cwDwYDVR0TAQH/\nBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEACipNr6ibOCtfvfRyCM2XMgeW7FF3\nKtNzZLqm+/0RwXiPZwxxYI2XVeTXLrZfrsBEK0oimeQhV/RCPe7t9ILGSNvPa8+Q\nHqrPt90FxQGiCSrUhgIz+VhbKRd9OJaTiOR/dnqA1To9TPnxjwBX2iEGbAyX5eqy\nbdeDuC0pB+2dSkJ9FtwaHjQfcBBlkk2xSHvvkWCVpd53xXBhVPRjzXPkTk1AOl9e\nuDDtaUAKndofh4I17IAYHrRUgLsFf/xrHfIGHFqhkVOz+iTHdKDD8wLMZlr6DVlk\nyNOdlIC1XZrvTsr4SyiMxvuNaArAePG26udlaoYznd8fU4hbp+4Nn1QCNpn3brVx\nvee5+Yz8zEv3iUGl+B5rjAdW3mcpB3qijKGdBF8qROBt6qYkmuMZEJP1oeI9LItX\nv6hpWRVA+9jP6Zjt56W/B+2ETKdIFg6eQBbGDkyAu7cv7OMsq/YstVN/HPxFg/p3\nrdxNVwqcnJ07cCVSnrbxdUHhL/Vcw8mBfDjez4BZUrFqen5O6r+WY1sM86Ex7IV5\nQTbRgaKiDW4SmqTu4++VOeHKp3pjm9UyFHB1jrPxJbm+P2lLn41n7LUU7Q35ce8D\nxBoDu3SIeoaF/e54+o4Pn0WDjs0zTV4YDMI2Zkt/QK5fLPx0VQBrxDl4MkcN7DnC\n1UV2bT78VPpeGn8=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/fixtures/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFyTCCA7GgAwIBAgIUVxjGOc+76Ux6YyeJUVSmTCrp7CowDQYJKoZIhvcNAQEL\nBQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G\nA1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe\nBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTI0MTAwMTA3NDMzNloY\nDzMwMjQwMjAyMDc0MzM2WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDzAN\nBgNVBAMMBmFnZW50MTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcw\nggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwNx4WYcYywrczaqneQ7n8\n5Y+dXT06dh9uunJyg42UEzKQ+Oa3uiFR8mrNd2P9zgPgdu/je94TU0su7h7xRHz+\ntIsr8S5FpeFNRzqe6q/20Qv2dJ+ZVqRvrJ0j9Kva2qgp5YGuD6e1ivcepJHHs7Cg\nT6XLliKkEaaxkX4p/9pp4vwsKV0bL92qhhWrWGxtoTDts9D/hBncTZf2WSRS26uC\n3XWnZqSx4gYRbb/1uFVdNOlGlqbypEMwpFOu7uYhA6o/Sj6euzzFrQlc3vjsGNSx\nLhW/uTFWF6ou9Zqwa4d3g9yxVCFQEAnfZzUGmo6DKu3wn2vFfaCS/2qN55LYZCq3\nVJpziPUFHlu8iPSEn1s3U8vwSqfehbjynQ45DjWeFkI9gBtAUGMJ0iXVgfyivO53\nJgvc3+pA621h216dcdn5hPilzHXQYS+xDv1DcM9wNbbZVee847N/88Xbi/FPOCIM\nqWVEihYq8aaKlLzXfETUaDFufmxx0m1hP7RjrklPunAgzRou9ombdVkVhnmTHH/n\nOqRjY7uwNXj0eW7wwZDdPxnGSBZV8ePUzzWDjEV6VMoaitI+lzfOUf+e/mZrQVof\nTMSynhFNnLssNqg5HKe4P45D0bWjz93+X0vYpNrKeFZSeHpTZYGESQ0A6baoGvw5\nLqgcT0aWxezzYF7IRBKvRwIDAQABo0IwQDAdBgNVHQ4EFgQUUj4+P8JxihhKlG1q\nzZP9KTqQhNwwHwYDVR0jBBgwFoAUMii3SZU8I+FEmIBfkoo/E3rMG+cwDQYJKoZI\nhvcNAQELBQADggIBAFwoYo6NKF9fyjI29341PQoivLT8QzD72nnoFtdemmDOPARE\nAKJtOyrVc/H0w4CtolK+gjTazVvVwv5FLZsRtvqoWGuzSGdgANGskHonT8iOZLyQ\nchwB0oC6iyyGmXkDnAAlsR7vp6duJRaHI9uDrO9SqRSbVF2TP5kdSzKoVK44t+bP\nc7/Cp5T9PBssHpXuq2y3vxFHAjJDnwuw8mXd1CSYw6GtDYj/eVMNukOwa1wZkDH2\no32V9c9oNceIFuI9O0F52H76U7Hnl7FGIO6BL67yeapkWTOl38j97+KHsXuMYe4f\nkVJnT6uUPuwva1zSc/X8Db9ZjAPG82nI9puMYZEQugjgdIB8PnkRbgwFXUvAXJ3U\n0CzymCnth0UviSsU0zluz87oOS8KH9jWI8Ul4d6wmiPRgwdt/sc/VvJ04RzM0v6s\nWmsGxjc3ff5rV5Cn/EF/s8nPjoVSlimoxrlmEIKz8tI1lHyccpDK7TzYdup4Z7Oy\n6Bt+7+PAyl974U4ptgSozjaKnOsw9OGIo9g6g4te9D5EDiHOC32Mja47i7UaM8en\nnmGH7W0L1Fj26CELlsrs5Chm0JXCyKxPcJK7pyKLAFOhXFYp5YsFyI2fGDmrQI58\nWLChV8nOTHWo1XrzKhTNB4tLPSXa6AcRYLEHpU0kbZyTC2La9zwyHVCnPMbn\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/fixtures/docker/dante/Dockerfile",
    "content": "FROM alpine:latest\n\n# Install Dante SOCKS server\nRUN apk add --no-cache dante-server\n\n# Create dante user\nRUN adduser -D -s /bin/false dante\n\n# Copy configuration\nCOPY danted.conf /etc/danted.conf\n\n# Create log directory\nRUN mkdir -p /var/log/dante && chown dante:dante /var/log/dante\n\n# Expose SOCKS port\nEXPOSE 1080\n\n# Run Dante\nCMD [\"danted\", \"-f\", \"/etc/danted.conf\"]"
  },
  {
    "path": "test/fixtures/docker/dante/danted.conf",
    "content": "# Dante SOCKS5 server configuration for testing\n\n# Log settings\nlogoutput: /var/log/dante/danted.log\ndebug: 1\n\n# Network interface configuration\ninternal: 0.0.0.0 port = 1080\nexternal: eth0\n\n# Authentication methods\nsocksmethod: none\nsocksmethod: username\n\n# User for username/password auth\nuser.privileged: root\nuser.unprivileged: dante\n\n# Client access rules\nclient pass {\n    from: 0.0.0.0/0 to: 0.0.0.0/0\n    log: error\n}\n\n# SOCKS rules\nsocks pass {\n    from: 0.0.0.0/0 to: 0.0.0.0/0\n    protocol: tcp udp\n    log: error\n}\n\n# Route rules\nroute {\n    from: 0.0.0.0/0 to: 0.0.0.0/0 via: eth0\n    protocol: tcp udp\n    proxyprotocol: socks_v5\n}"
  },
  {
    "path": "test/fixtures/docker/docker-compose.yml",
    "content": "services:\n  # SOCKS5 proxy without authentication\n  socks5-no-auth:\n    image: serjs/go-socks5-proxy:latest\n    container_name: socks5-no-auth\n    environment:\n      - PROXY_PORT=1080\n      - REQUIRE_AUTH=false\n    ports:\n      - \"1080:1080\"\n    networks:\n      - test-network\n\n  # SOCKS5 proxy with username/password authentication\n  socks5-auth:\n    image: serjs/go-socks5-proxy:latest\n    container_name: socks5-auth\n    environment:\n      - PROXY_USER=testuser\n      - PROXY_PASSWORD=testpass\n      - PROXY_PORT=1081\n    ports:\n      - \"1081:1081\"\n    networks:\n      - test-network\n\n  # Alternative: Dante SOCKS5 server (more configurable)\n  dante-socks5:\n    build:\n      context: ./dante\n      dockerfile: Dockerfile\n    container_name: dante-socks5\n    ports:\n      - \"1082:1080\"\n    networks:\n      - test-network\n    volumes:\n      - ./dante/danted.conf:/etc/danted.conf:ro\n\n  # HTTP test server\n  http-server:\n    image: node:20-alpine\n    container_name: http-test-server\n    working_dir: /app\n    volumes:\n      - ../servers/http-server.js:/app/server.js:ro\n    command: node server.js\n    ports:\n      - \"8080:8080\"\n    networks:\n      - test-network\n\n  # HTTPS test server\n  https-server:\n    image: node:20-alpine\n    container_name: https-test-server\n    working_dir: /app\n    volumes:\n      - ../servers/https-server.js:/app/server.js:ro\n      - ../certs:/app/certs:ro\n    command: node server.js\n    ports:\n      - \"8443:8443\"\n    networks:\n      - test-network\n\n  # Echo server for testing\n  echo-server:\n    image: ealen/echo-server:latest\n    container_name: echo-server\n    environment:\n      - PORT=3000\n    ports:\n      - \"3000:3000\"\n    networks:\n      - test-network\n\n  # Blocked target (for testing connection failures)\n  blocked-server:\n    image: alpine:latest\n    container_name: blocked-server\n    command: sleep infinity\n    networks:\n      - isolated-network\n\nnetworks:\n  test-network:\n    driver: bridge\n  isolated-network:\n    driver: bridge\n    internal: true"
  },
  {
    "path": "test/fixtures/duplicate-debug.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { request } = require('../..')\n\n// Simulate the scenario where diagnostics module is loaded multiple times\n// This mimics having both Node.js built-in undici and undici as dependency\ndelete require.cache[require.resolve('../../lib/core/diagnostics.js')]\nrequire('../../lib/core/diagnostics.js')\n\nconst server = createServer({ joinDuplicateHeaders: true }, (_req, res) => {\n  res.writeHead(200, { 'Content-Type': 'text/plain' })\n  res.end('hello world')\n})\n\nserver.listen(0, () => {\n  const { port, address, family } = server.address()\n  const hostname = family === 'IPv6' ? `[${address}]` : address\n  request(`http://${hostname}:${port}`)\n    .then(res => res.body.dump())\n    .then(() => {\n      server.close()\n    })\n})\n"
  },
  {
    "path": "test/fixtures/fetch.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { fetch } = require('../..')\n\nconst server = createServer({ joinDuplicateHeaders: true }, (_req, res) => {\n  res.writeHead(200, { 'Content-Type': 'text/plain' })\n  res.end('hello world')\n})\n\nserver.listen(0, () => {\n  const { port, address, family } = server.address()\n  const hostname = family === 'IPv6' ? `[${address}]` : address\n  fetch(`http://${hostname}:${port}`)\n    .then(\n      res => res.body.cancel(),\n      () => {}\n    )\n    .then(() => {\n      server.close()\n    })\n})\n"
  },
  {
    "path": "test/fixtures/interceptors/retry-event-loop.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst {\n  Client,\n  interceptors: { retry }\n} = require('../../..')\n\nconst server = createServer({ joinDuplicateHeaders: true })\n\nserver.on('request', (req, res) => {\n  res.writeHead(418, { 'Content-Type': 'text/plain' })\n  res.end('teapot')\n})\n\nserver.listen(0)\nonce(server, 'listening').then(() => {\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(\n    retry({\n      maxTimeout: 1000,\n      maxRetries: 3,\n      statusCodes: [418]\n    })\n  )\n\n  return client.request({\n    method: 'GET',\n    path: '/'\n  })\n})\n"
  },
  {
    "path": "test/fixtures/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCwNx4WYcYywrcz\naqneQ7n85Y+dXT06dh9uunJyg42UEzKQ+Oa3uiFR8mrNd2P9zgPgdu/je94TU0su\n7h7xRHz+tIsr8S5FpeFNRzqe6q/20Qv2dJ+ZVqRvrJ0j9Kva2qgp5YGuD6e1ivce\npJHHs7CgT6XLliKkEaaxkX4p/9pp4vwsKV0bL92qhhWrWGxtoTDts9D/hBncTZf2\nWSRS26uC3XWnZqSx4gYRbb/1uFVdNOlGlqbypEMwpFOu7uYhA6o/Sj6euzzFrQlc\n3vjsGNSxLhW/uTFWF6ou9Zqwa4d3g9yxVCFQEAnfZzUGmo6DKu3wn2vFfaCS/2qN\n55LYZCq3VJpziPUFHlu8iPSEn1s3U8vwSqfehbjynQ45DjWeFkI9gBtAUGMJ0iXV\ngfyivO53Jgvc3+pA621h216dcdn5hPilzHXQYS+xDv1DcM9wNbbZVee847N/88Xb\ni/FPOCIMqWVEihYq8aaKlLzXfETUaDFufmxx0m1hP7RjrklPunAgzRou9ombdVkV\nhnmTHH/nOqRjY7uwNXj0eW7wwZDdPxnGSBZV8ePUzzWDjEV6VMoaitI+lzfOUf+e\n/mZrQVofTMSynhFNnLssNqg5HKe4P45D0bWjz93+X0vYpNrKeFZSeHpTZYGESQ0A\n6baoGvw5LqgcT0aWxezzYF7IRBKvRwIDAQABAoICABfIoK15reQdBtgQPfQrZPd+\nznb5ZjG1TsHFtXvCSMIjIzCQ/6btnuCuHP81bZAMldZehztHdS5bkCq55gA/c7V3\nDc+1Aj9RR8sD4aQgXfasuXYewInUOWZ/QEhhli54U7kv6mRhZYvpwTfoE2sGVEEW\n7vQ/A9bsMPkHf6VQjJy9D7cwMApi2ALTjSouyZe0aWOz4PIT1N+4s1mDJ5VtY8VK\neb5J6tG9hX8ltoKGSjNF2HR4Eflu9Uij7U2Pngz3rytSrIgFEotFsx1PVP6czVxK\nsZHKf5+0mvoymRnVsZeOeyOODN7/Ay4dgnktNC39Bddz1Pp3XcxpX+reRiIhxuf0\n0LXk4DUt1w7wNOaa15adg6v38oxAkq7kOidxTs5+hOQzafyODGYa3Vw+KaAb1le9\nNZikgBXWLirXhlyDF5YbfAKsk+8JhtJc/BRVp4DNdUqz45jFb+VRM9soXhwK/2Za\n/PC9I3w5ejz8d0Dd11sT9ySI0A8jv5qtxRbqvzbSYeZav1cAkWCqkIpdjKhdGknO\nywWae1CDU0pFdNx9iSbuse7bTS7SAqXuGLTZQtZECigP2vjW1x5N1ZT/Mas0UmR7\nEUgzuNNA87GnlQPKOnBsGo79RiKtoKrO7FJjYR/43M4aDlg0MSEi1s5kcwVOk8L9\n3wZi/g2gq6EGtcwhOBAdAoIBAQDyy8OqZHq0tG6qqkJaedSdj1aW+fdjtVv/Vxbd\nR5R98JONRwwYjppdi7U1sbcRFsqgmR0fVyPrLvx+KemqTK6PA6J49XlwmL/DuYPS\n3y9Va4Fl6HkaiaUG3pK5U3Z0jDUgNnCjTXTgghHp15ooepQNFIc0EwhGBDIOPQME\n0ecQD9sFW7a9H0I4u1HnjLnul+ofGf2vfwBkI/n5mNjn7k3tng2zvlLd1cHbgdou\nO3dc5nEyCMCdqDze0S9GS9mf2rC3IQWNsCV7aA5pdHWxpYTz3qu7fIhVckHK5s8k\nM5joOjxG70fX/z14L50Gb4G02rLWOEoDy2iyYSKmvIO3X5mlAoIBAQC5zGi2/Abm\n9l0ZJZTNctwwm0hB9/Ux4C4CrPhi9kqd2Z+i+XsyDrCQ0h/ZSKt3VHdpnItlCpK2\nPaSQ9iFS1DUmxxBrZ1hyJjgw9WyXlzAbFfTo4iUt7qJMiOztfsubap9VzbkknE4G\nMHQ/hDRPMkh6pwpIHjqRYbg4JNxRey8GtO7zwhDKdURi4paoboWw0VDYxy1MIz9g\nd3lE+vr4QDB7cANFJRYaBI3YDxaxpSLSXoOmhbhxD3+11iCDbQaGYIORb23ilESK\n3//alSeIhaWzAb2+hwTEI4P7foLKInVx7i/W6kTTlJ2rZumTGUGWIMjoEmQ+w2KO\nDBAYwSNlm9l7AoIBAQDQ7Hogg3n7SU/5V6zlQfSs6AzwuYQhrovNetlX7CJhBMVT\nSpGkCAHZAUEbRSNsdxpBe7/NmiR0Weg3gEVrn7SNp+kFAOZQ93/8IgTHTfnjHTEp\nyhN7vHnfIWNMSf+iZovIflAKlbo+/m3/tOEYd/IyFzoIm2ABL9cK3YFdgmm8Loif\nYb4rm1xWiQn/n97W6q4xuSHNBBIIGdUe7GGpoiw4jkroIpwX+7pm8qQWKGGb9Ufu\ncA2fHIfUjFiLuvU3Uu3Bh47Jz4tRV8cfA3HLPczcNP29xXljXYAz4szYL/YhzwrT\nV0+RFDeG1iHeydDpGU/Oen1mKoCbDm7M32bQQllpAoIBADnEK+p4gUzd3CQtYw5d\nX8hc/yJDjaBsKuH6FV/vY1OgjdmF55+woYTlT7Gmvmjjghz75vsLRoISuE+5trKh\n98SOr7Q09XLIH0BZjeGzx+kj8nlVlmmpgBx7le5hNbykcdWjmKShVEDoX7w/xmO5\nJn+73558h4kb8MLD8xwCSKS1LHXtKHtJ6nE0MdM8SaSn75L2mkbJzrKXcsTXo5/7\nlRdLxDiDR1PfhppeVpf019bAO/5SJP5B61sFsCYsh5LP/xgApRGFN6pV6p5zMU9o\n/hOhvvS11e2FfUt8Ef32qL07aPRQ8gU2d68K2CQ7/gBHQS+mSDSbWtD/PyHzKqY0\nxnECggEAf9IVxlK1IX2FsFbNIG1CCVDkj55w5+jHuVdYAEUYIDwQEUMYhQ3GpgsC\nDgFWyddlz1ni8gGvZgwvIZTNA0vz3SEKuOAbcdKfqRspuKIksfHnYikr2UUOBH5a\n2hCUirh6UC/5cN0FBC0KV9fJTqiKoUqCI4HEWD6uMPzv892rP7Q80CAkAdalm9Ui\nk8ZSfwrqfvAx/iE84VrAKKRxhegyQ2+KYZGc0EMlWXz6/GjrVyUKFqDVjaidmlEj\nHBXxEWVKcsTit22GsU4Pl8mZS8DRIZm+wwIp60uP98VtXXBiPPEkea1t9D4T5Gi7\nzhkPVDbGIYpzFskiOGNjvnvBhwVdVg==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/socks5-test-server.js",
    "content": "'use strict'\n\nconst net = require('node:net')\nconst { AUTH_METHODS, REPLY_CODES } = require('../../lib/core/socks5-client')\n\n/**\n * Test SOCKS5 server for unit tests\n * Implements SOCKS5 protocol with optional authentication\n */\nclass TestSocks5Server {\n  constructor (options = {}) {\n    this.options = options\n    this.server = null\n    this.connections = new Set()\n    this.requireAuth = options.requireAuth || false\n    this.validCredentials = options.credentials || { username: 'test', password: 'pass' }\n  }\n\n  async listen (port = 0) {\n    return new Promise((resolve, reject) => {\n      this.server = net.createServer((socket) => {\n        this.connections.add(socket)\n        this.handleConnection(socket)\n\n        socket.on('close', () => {\n          this.connections.delete(socket)\n        })\n      })\n\n      this.server.listen(port, (err) => {\n        if (err) {\n          reject(err)\n        } else {\n          resolve(this.server.address())\n        }\n      })\n    })\n  }\n\n  handleConnection (socket) {\n    let state = 'handshake'\n    let buffer = Buffer.alloc(0)\n\n    socket.on('data', (data) => {\n      buffer = Buffer.concat([buffer, data])\n\n      if (state === 'handshake') {\n        this.handleHandshake(socket, buffer, (newBuffer, method) => {\n          buffer = newBuffer\n          if (method === AUTH_METHODS.NO_AUTH) {\n            state = 'connect'\n          } else if (method === AUTH_METHODS.USERNAME_PASSWORD) {\n            state = 'auth'\n          }\n        })\n      } else if (state === 'auth') {\n        this.handleAuth(socket, buffer, (newBuffer, success) => {\n          buffer = newBuffer\n          if (success) {\n            state = 'connect'\n          } else {\n            socket.end()\n          }\n        })\n      } else if (state === 'connect') {\n        this.handleConnect(socket, buffer, (newBuffer) => {\n          buffer = newBuffer\n          state = 'relay'\n        })\n      }\n    })\n\n    socket.on('error', () => {\n      // Handle socket errors silently\n    })\n  }\n\n  handleHandshake (socket, buffer, callback) {\n    if (buffer.length >= 2) {\n      const version = buffer[0]\n      const nmethods = buffer[1]\n\n      if (version === 0x05 && buffer.length >= 2 + nmethods) {\n        const methods = Array.from(buffer.subarray(2, 2 + nmethods))\n\n        // Select authentication method\n        let selectedMethod\n        if (this.requireAuth && methods.includes(AUTH_METHODS.USERNAME_PASSWORD)) {\n          selectedMethod = AUTH_METHODS.USERNAME_PASSWORD\n        } else if (!this.requireAuth && methods.includes(AUTH_METHODS.NO_AUTH)) {\n          selectedMethod = AUTH_METHODS.NO_AUTH\n        } else {\n          selectedMethod = AUTH_METHODS.NO_ACCEPTABLE\n        }\n\n        socket.write(Buffer.from([0x05, selectedMethod]))\n        callback(buffer.subarray(2 + nmethods), selectedMethod)\n      }\n    }\n  }\n\n  handleAuth (socket, buffer, callback) {\n    if (buffer.length >= 2) {\n      const version = buffer[0]\n      if (version !== 0x01) {\n        socket.write(Buffer.from([0x01, 0x01])) // Failure\n        callback(buffer, false)\n        return\n      }\n\n      const usernameLen = buffer[1]\n      if (buffer.length >= 3 + usernameLen) {\n        const username = buffer.subarray(2, 2 + usernameLen).toString()\n        const passwordLen = buffer[2 + usernameLen]\n\n        if (buffer.length >= 3 + usernameLen + passwordLen) {\n          const password = buffer.subarray(3 + usernameLen, 3 + usernameLen + passwordLen).toString()\n\n          const success = username === this.validCredentials.username &&\n                         password === this.validCredentials.password\n\n          socket.write(Buffer.from([0x01, success ? 0x00 : 0x01]))\n          callback(buffer.subarray(3 + usernameLen + passwordLen), success)\n        }\n      }\n    }\n  }\n\n  handleConnect (socket, buffer, callback) {\n    if (buffer.length >= 4) {\n      const version = buffer[0]\n      const cmd = buffer[1]\n      const atyp = buffer[3]\n\n      if (version === 0x05 && cmd === 0x01) {\n        let addressLength = 0\n        if (atyp === 0x01) {\n          addressLength = 4 // IPv4\n        } else if (atyp === 0x03) {\n          if (buffer.length >= 5) {\n            addressLength = 1 + buffer[4] // Domain length + domain\n          } else {\n            return // Not enough data\n          }\n        } else if (atyp === 0x04) {\n          addressLength = 16 // IPv6\n        }\n\n        if (buffer.length >= 4 + addressLength + 2) {\n          // Extract target address and port\n          let targetHost\n          let offset = 4\n\n          if (atyp === 0x01) {\n            targetHost = Array.from(buffer.subarray(offset, offset + 4)).join('.')\n            offset += 4\n          } else if (atyp === 0x03) {\n            const domainLen = buffer[offset]\n            offset += 1\n            targetHost = buffer.subarray(offset, offset + domainLen).toString()\n            offset += domainLen\n          }\n\n          const targetPort = buffer.readUInt16BE(offset)\n\n          // Simulate connection failure if requested\n          if (this.options.simulateFailure) {\n            const response = Buffer.concat([\n              Buffer.from([0x05, REPLY_CODES.CONNECTION_REFUSED, 0x00, 0x01]),\n              Buffer.from([0, 0, 0, 0]),\n              Buffer.from([0, 0])\n            ])\n            socket.write(response)\n            socket.end()\n            return\n          }\n\n          // Connect to target\n          const targetSocket = net.connect(targetPort, targetHost)\n\n          targetSocket.on('connect', () => {\n            // Send success response\n            const response = Buffer.concat([\n              Buffer.from([0x05, 0x00, 0x00, 0x01]), // VER, REP, RSV, ATYP\n              Buffer.from([127, 0, 0, 1]), // Bind address (localhost)\n              Buffer.allocUnsafe(2) // Bind port\n            ])\n            response.writeUInt16BE(targetPort, response.length - 2)\n            socket.write(response)\n\n            // Start relaying data\n            socket.pipe(targetSocket)\n            targetSocket.pipe(socket)\n\n            callback(buffer.subarray(4 + addressLength + 2))\n          })\n\n          targetSocket.on('error', () => {\n            // Send connection refused\n            const response = Buffer.concat([\n              Buffer.from([0x05, REPLY_CODES.CONNECTION_REFUSED, 0x00, 0x01]),\n              Buffer.from([0, 0, 0, 0]),\n              Buffer.from([0, 0])\n            ])\n            socket.write(response)\n            socket.end()\n          })\n        }\n      }\n    }\n  }\n\n  async close () {\n    if (this.server) {\n      // Close all connections\n      for (const socket of this.connections) {\n        socket.destroy()\n      }\n\n      return new Promise((resolve) => {\n        this.server.close(resolve)\n      })\n    }\n  }\n}\n\nmodule.exports = { TestSocks5Server }\n"
  },
  {
    "path": "test/fixtures/undici.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { request } = require('../..')\n\nconst server = createServer({ joinDuplicateHeaders: true }, (_req, res) => {\n  res.writeHead(200, { 'Content-Type': 'text/plain' })\n  res.end('hello world')\n})\n\nserver.listen(0, () => {\n  const { port, address, family } = server.address()\n  const hostname = family === 'IPv6' ? `[${address}]` : address\n  request(`http://${hostname}:${port}`)\n    .then(res => res.body.dump())\n    .then(() => {\n      server.close()\n    })\n})\n"
  },
  {
    "path": "test/fixtures/websocket.js",
    "content": "'use strict'\n\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\nconst server = new WebSocketServer({ port: 0 })\n\nserver.on('connection', ws => {\n  ws.close(1000, 'goodbye')\n})\nserver.on('listening', () => {\n  const { port } = server.address()\n  const ws = new WebSocket(`ws://localhost:${port}`, 'chat')\n\n  ws.addEventListener('close', () => {\n    server.close()\n  })\n})\n"
  },
  {
    "path": "test/fuzzing/client/client-fuzz-body.js",
    "content": "'use strict'\n\nconst { request, errors } = require('../../..')\n\nconst acceptableCodes = [\n  'ERR_INVALID_ARG_TYPE'\n]\n\nasync function fuzz (address, results, buf) {\n  const body = buf\n  results.body = body\n  try {\n    const data = await request(address, { body })\n    data.body.destroy().on('error', () => {})\n  } catch (err) {\n    results.err = err\n    // Handle any undici errors\n    if (Object.values(errors).some(undiciError => err instanceof undiciError)) {\n      // Okay error\n    } else if (!acceptableCodes.includes(err.code)) {\n      throw err\n    }\n  }\n}\n\nmodule.exports = fuzz\n"
  },
  {
    "path": "test/fuzzing/client/client-fuzz-headers.js",
    "content": "'use strict'\n\nconst { request, errors } = require('../../..')\n\nconst acceptableCodes = [\n  'ERR_INVALID_ARG_TYPE'\n]\n\nasync function fuzz (address, results, buf) {\n  const headers = { buf: buf.toString() }\n  results.body = headers\n  try {\n    const data = await request(address, { headers })\n    data.body.destroy().on('error', () => {})\n  } catch (err) {\n    results.err = err\n    // Handle any undici errors\n    if (Object.values(errors).some(undiciError => err instanceof undiciError)) {\n      // Okay error\n    } else if (!acceptableCodes.includes(err.code)) {\n      throw err\n    }\n  }\n}\n\nmodule.exports = fuzz\n"
  },
  {
    "path": "test/fuzzing/client/client-fuzz-options.js",
    "content": "'use strict'\n\nconst { request, errors } = require('../../..')\n\nconst acceptableCodes = [\n  'ERR_INVALID_URL',\n  // These are included because '\\\\ABC' is interpreted as a Windows UNC path and can cause these errors.\n  'ENOTFOUND',\n  'EAI_AGAIN',\n  'ECONNREFUSED'\n]\n\nasync function fuzz (address, results, buf) {\n  const optionKeys = ['body', 'path', 'method', 'opaque', 'upgrade', buf]\n  const options = {}\n  for (const optionKey of optionKeys) {\n    if (Math.random() < 0.5) {\n      options[optionKey] = buf.toString()\n    }\n  }\n  results.options = options\n  try {\n    const data = await request(address, options)\n    data.body.destroy().on('error', () => {})\n  } catch (err) {\n    results.err = err\n    // Handle any undici errors\n    if (Object.values(errors).some(undiciError => err instanceof undiciError)) {\n      // Okay error\n    } else if (!acceptableCodes.includes(err.code)) {\n      throw err\n    }\n  }\n}\n\nmodule.exports = fuzz\n"
  },
  {
    "path": "test/fuzzing/client/index.js",
    "content": "'use strict'\n\nmodule.exports = {\n  clientFuzzBody: require('./client-fuzz-body'),\n  clientFuzzHeaders: require('./client-fuzz-headers'),\n  clientFuzzOptions: require('./client-fuzz-options')\n}\n"
  },
  {
    "path": "test/fuzzing/fuzzing.test.js",
    "content": "'use strict'\n\nconst { once } = require('node:events')\nconst fc = require('fast-check')\nconst netServer = require('./server')\nconst { describe, before, after, test } = require('node:test')\nconst {\n  clientFuzzBody,\n  clientFuzzHeaders,\n  clientFuzzOptions\n} = require('./client')\n\n// Detect if running in CI (here we use GitHub Workflows)\n// https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables\nconst isCI = process.env.CI === 'true'\n\nfc.configureGlobal({\n  interruptAfterTimeLimit: isCI ? 60_000 /* 1 minute */ : 10_000 /* 10 seconds */,\n  numRuns: Number.MAX_SAFE_INTEGER\n})\n\ndescribe('fuzzing', { timeout: 600_000 /* 10 minutes */ }, () => {\n  before(async () => {\n    netServer.listen(0)\n    await once(netServer, 'listening')\n  })\n\n  after(() => {\n    netServer.close()\n  })\n\n  test('body', async () => {\n    const address = `http://localhost:${netServer.address().port}`\n    await fc.assert(\n      fc.asyncProperty(fc.uint8Array(), async (body) => {\n        body = Buffer.from(body)\n        const results = {}\n        await clientFuzzBody(address, results, body)\n      })\n    )\n  })\n\n  test('headers', async () => {\n    const address = `http://localhost:${netServer.address().port}`\n    await fc.assert(\n      fc.asyncProperty(fc.uint8Array(), async (body) => {\n        body = Buffer.from(body)\n        const results = {}\n        await clientFuzzHeaders(address, results, body)\n      })\n    )\n  })\n\n  test('options', async () => {\n    const address = `http://localhost:${netServer.address().port}`\n    await fc.assert(\n      fc.asyncProperty(fc.uint8Array(), async (body) => {\n        body = Buffer.from(body)\n        const results = {}\n        await clientFuzzOptions(address, results, body)\n      })\n    )\n  })\n})\n"
  },
  {
    "path": "test/fuzzing/server/index.js",
    "content": "'use strict'\n\nconst net = require('node:net')\nconst serverFuzzFns = [\n  require('./server-fuzz-append-data'),\n  require('./server-fuzz-split-data')\n]\n\nconst netServer = net.createServer(socket => {\n  socket.on('data', data => {\n    serverFuzzFns[(Math.random() * 2) | 0](socket, data)\n  })\n})\n\nmodule.exports = netServer\n"
  },
  {
    "path": "test/fuzzing/server/server-fuzz-append-data.js",
    "content": "'use strict'\n\nfunction appendData (socket, data) {\n  socket.end('HTTP/1.1 200 OK' + data)\n}\n\nmodule.exports = appendData\n"
  },
  {
    "path": "test/fuzzing/server/server-fuzz-split-data.js",
    "content": "'use strict'\n\nfunction splitData (socket, data) {\n  const lines = [\n    'HTTP/1.1 200 OK',\n    'Date: Sat, 09 Oct 2010 14:28:02 GMT',\n    'Connection: close',\n    '',\n    data\n  ]\n  for (const line of lines.join('\\r\\n').split(data)) {\n    socket.write(line)\n  }\n  socket.end()\n}\n\nmodule.exports = splitData\n"
  },
  {
    "path": "test/gc.js",
    "content": "'use strict'\n/* global WeakRef, FinalizationRegistry */\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:net')\nconst { Client, Pool } = require('..')\n\nconst hasGC = typeof global.gc !== 'undefined'\n\nif (hasGC) {\n  setInterval(() => {\n    global.gc()\n  }, 100).unref()\n}\n\ntest('gc should collect the client if, and only if, there are no active sockets', async t => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=1s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n\n  let weakRef\n  let disconnected = false\n\n  const registry = new FinalizationRegistry((data) => {\n    t.strictEqual(data, 'test')\n    t.strictEqual(disconnected, true)\n    t.strictEqual(weakRef.deref(), undefined)\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      keepAliveTimeoutThreshold: 100\n    })\n    client.once('disconnect', () => {\n      disconnected = true\n    })\n\n    weakRef = new WeakRef(client)\n    registry.register(client, 'test')\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.resume()\n    })\n  })\n\n  await t.completed\n})\n\ntest('gc should collect the pool if, and only if, there are no active sockets', async t => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer((socket) => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 0\\r\\n')\n    socket.write('Keep-Alive: timeout=1s\\r\\n')\n    socket.write('Connection: keep-alive\\r\\n')\n    socket.write('\\r\\n\\r\\n')\n  })\n  after(() => server.close())\n\n  let weakRef\n  let disconnected = false\n\n  const registry = new FinalizationRegistry((data) => {\n    t.strictEqual(data, 'test')\n    t.strictEqual(disconnected, true)\n    t.strictEqual(weakRef.deref(), undefined)\n  })\n\n  server.listen(0, () => {\n    const pool = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      keepAliveTimeoutThreshold: 500\n    })\n\n    pool.once('disconnect', () => {\n      disconnected = true\n    })\n\n    weakRef = new WeakRef(pool)\n    registry.register(pool, 'test')\n\n    pool.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { body }) => {\n      t.ifError(err)\n      body.resume()\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/get-head-body.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { kConnect } = require('../lib/core/symbols')\nconst { kBusy } = require('../lib/core/symbols')\nconst { wrapWithAsyncIterable } = require('./utils/async-iterators')\n\ntest('GET and HEAD with body should reset connection', async (t) => {\n  t = tspl(t, { plan: 8 + 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      t.ok(true, 'pass')\n    })\n\n    client.request({\n      path: '/',\n      body: 'asd',\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    const emptyBody = new Readable({\n      read () {}\n    })\n    emptyBody.push(null)\n    client.request({\n      path: '/',\n      body: emptyBody,\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      body: new Readable({\n        read () {\n          this.push(null)\n        }\n      }),\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      body: new Readable({\n        read () {\n          this.push('asd')\n          this.push(null)\n        }\n      }),\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      body: [],\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      body: wrapWithAsyncIterable(new Readable({\n        read () {\n          this.push(null)\n        }\n      })),\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      body: wrapWithAsyncIterable(new Readable({\n        read () {\n          this.push('asd')\n          this.push(null)\n        }\n      })),\n      method: 'GET'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n  })\n\n  await t.completed\n})\n\n// TODO: Avoid external dependency.\n// test('GET with body should work when target parses body as request', async (t) => {\n//   t = tspl(t, { plan: 4 })\n\n//   // This URL will send double responses when receiving a\n//   // GET request with body.\n//   const client = new Client('http://feeds.bbci.co.uk')\n//   after(() => client.close())\n\n//   client.request({ method: 'GET', path: '/news/rss.xml', body: 'asd' }, (err, data) => {\n//     t.ifError(err)\n//     t.strictEqual(data.statusCode, 200)\n//     data.body.resume()\n//   })\n//   client.request({ method: 'GET', path: '/news/rss.xml', body: 'asd' }, (err, data) => {\n//     t.ifError(err)\n//     t.strictEqual(data.statusCode, 200)\n//     data.body.resume()\n//   })\n\n// await t.completed\n// })\n\ntest('HEAD should reset connection', async (t) => {\n  t = tspl(t, { plan: 8 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.once('disconnect', () => {\n      t.ok(true, 'pass')\n    })\n\n    client.request({\n      path: '/',\n      method: 'HEAD'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n    })\n    t.strictEqual(client[kBusy], true)\n\n    client.request({\n      path: '/',\n      method: 'HEAD'\n    }, (err, data) => {\n      t.ifError(err)\n      data.body.resume()\n      client.once('disconnect', () => {\n        client[kConnect](() => {\n          client.request({\n            path: '/',\n            method: 'HEAD'\n          }, (err, data) => {\n            t.ifError(err)\n            data.body.resume()\n            data.body.on('end', () => {\n              t.ok(true, 'pass')\n            })\n          })\n          t.strictEqual(client[kBusy], true)\n        })\n      })\n    })\n    t.strictEqual(client[kBusy], true)\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/h2c-client.js",
    "content": "'use strict'\n\nconst { createServer, createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\nconst { test } = require('node:test')\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst pem = require('@metcoder95/https-pem')\n\nconst { H2CClient, Client } = require('..')\n\ntest('Should throw if no h2c origin', async t => {\n  const planner = tspl(t, { plan: 1 })\n\n  planner.throws(() => new H2CClient('https://localhost/'))\n\n  await planner.completed\n})\n\ntest('Should throw if pipelining greather than concurrent streams', async t => {\n  const planner = tspl(t, { plan: 1 })\n\n  planner.throws(() => new H2CClient('http://localhost/', { pipelining: 10, maxConcurrentStreams: 5 }))\n\n  await planner.completed\n})\n\ntest('Should support h2c connection', async t => {\n  const planner = tspl(t, { plan: 6 })\n  let authority = ''\n\n  const server = createServer((req, res) => {\n    planner.equal(req.headers[':authority'], authority)\n    planner.equal(req.headers[':method'], 'GET')\n    planner.equal(req.headers[':path'], '/')\n    planner.equal(req.headers[':scheme'], 'http')\n    res.writeHead(200)\n    res.end('Hello, world!')\n  })\n\n  server.listen()\n  await once(server, 'listening')\n  authority = `localhost:${server.address().port}`\n  const client = new H2CClient(`http://${authority}/`)\n\n  t.after(() => client.close())\n  t.after(() => server.close())\n\n  const response = await client\n    .request({ path: '/', method: 'GET' })\n  planner.equal(response.statusCode, 200)\n  planner.equal(await response.body.text(), 'Hello, world!')\n})\n\ntest('Should support h2c connection with body', async t => {\n  const planner = tspl(t, { plan: 3 })\n  const bodyChunks = []\n\n  const server = createServer((req, res) => {\n    req.on('data', chunk => bodyChunks.push(chunk))\n    req.on('end', () => {\n      res.end('Hello, world!')\n    })\n    res.writeHead(200, {\n      'Content-Type': 'text/plain'\n    })\n  })\n\n  server.listen()\n  await once(server, 'listening')\n  const client = new H2CClient(`http://localhost:${server.address().port}/`)\n\n  t.after(() => client.close())\n  t.after(() => server.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    body: 'Hello, world!'\n  })\n  planner.equal(response.statusCode, 200)\n  planner.equal(await response.body.text(), 'Hello, world!')\n  planner.equal(Buffer.concat(bodyChunks).toString(), 'Hello, world!')\n})\n\ntest('Should reject request if not h2c supported', async t => {\n  const planner = tspl(t, { plan: 1 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }), (req, res) => {\n    res.writeHead(200)\n    res.end('Hello, world!')\n  })\n\n  server.listen()\n  await once(server, 'listening')\n  const client = new H2CClient(`http://localhost:${server.address().port}/`)\n\n  t.after(() => client.close())\n  t.after(() => server.close())\n\n  planner.rejects(\n    client.request({ path: '/', method: 'GET' }),\n    'SocketError: other side closed'\n  )\n})\n\ntest('Connect to h2c server over a unix domain socket', { skip: process.platform === 'win32' }, async t => {\n  const planner = tspl(t, { plan: 6 })\n  const { mkdtemp, rm } = require('node:fs/promises')\n  const { join } = require('node:path')\n  const { tmpdir } = require('node:os')\n\n  const tmpDir = await mkdtemp(join(tmpdir(), 'h2c-client-'))\n  const socketPath = join(tmpDir, 'server.sock')\n  const authority = 'localhost'\n\n  const server = createServer((req, res) => {\n    planner.equal(req.headers[':authority'], authority)\n    planner.equal(req.headers[':method'], 'GET')\n    planner.equal(req.headers[':path'], '/')\n    planner.equal(req.headers[':scheme'], 'http')\n    res.writeHead(200)\n    res.end('Hello, world!')\n  })\n\n  server.listen(socketPath)\n  await once(server, 'listening')\n  const client = new H2CClient(`http://${authority}/`, {\n    socketPath\n  })\n\n  const response = await client.request({ path: '/', method: 'GET' })\n  planner.equal(response.statusCode, 200)\n  planner.equal(await response.body.text(), 'Hello, world!')\n\n  t.after(async () => {\n    await rm(tmpDir, { recursive: true })\n    client.close()\n    server.close()\n  })\n})\n\ntest('Should throw if bad useH2c has been passed', async t => {\n  t = tspl(t, { plan: 1 })\n\n  t.throws(() => {\n    // eslint-disable-next-line\n    new Client('https://localhost:1000', {\n      useH2c: 'true'\n    })\n  }, {\n    message: 'useH2c must be a valid boolean value'\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/headers-as-array.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\n\ntest('handle headers as array', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const headers = ['a', '1', 'b', '2', 'c', '3']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers.a, '1')\n    t.strictEqual(req.headers.b, '2')\n    t.strictEqual(req.headers.c, '3')\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, () => { })\n  })\n\n  await t.completed\n})\n\ntest('handle multi-valued headers as array', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const headers = ['a', '1', 'b', '2', 'c', '3', 'd', '4', 'd', '5']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers.a, '1')\n    t.strictEqual(req.headers.b, '2')\n    t.strictEqual(req.headers.c, '3')\n    t.strictEqual(req.headers.d, '4, 5')\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, () => { })\n  })\n\n  await t.completed\n})\n\ntest('handle headers with array', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const headers = { a: '1', b: '2', c: '3', d: ['4'] }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers.a, '1')\n    t.strictEqual(req.headers.b, '2')\n    t.strictEqual(req.headers.c, '3')\n    t.strictEqual(req.headers.d, '4')\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, () => { })\n  })\n\n  await t.completed\n})\n\ntest('handle multi-valued headers', async (t) => {\n  t = tspl(t, { plan: 4 })\n  const headers = { a: '1', b: '2', c: '3', d: ['4', '5'] }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(req.headers.a, '1')\n    t.strictEqual(req.headers.b, '2')\n    t.strictEqual(req.headers.c, '3')\n    t.strictEqual(req.headers.d, '4, 5')\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, () => { })\n  })\n\n  await t.completed\n})\n\ntest('fail if headers array is odd', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = ['a', '1', 'b', '2', 'c', '3', 'd']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'headers array must be even')\n    })\n  })\n\n  await t.completed\n})\n\ntest('fail if headers is not an object or an array', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = 'not an object or an array'\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'headers must be an object or an array')\n    })\n  })\n\n  await t.completed\n})\n\ntest('fail if duplicate content-length headers (different case)', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = ['Content-Length', '5', 'content-length', '0']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers,\n      body: 'hello'\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'duplicate content-length header')\n    })\n  })\n\n  await t.completed\n})\n\ntest('fail if duplicate content-length headers (same case)', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = ['content-length', '5', 'content-length', '0']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      headers,\n      body: 'hello'\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'duplicate content-length header')\n    })\n  })\n\n  await t.completed\n})\n\ntest('fail if duplicate host headers (different case)', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = ['Host', 'example.com', 'host', 'evil.com']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'duplicate host header')\n    })\n  })\n\n  await t.completed\n})\n\ntest('fail if duplicate host headers (same case)', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const headers = ['host', 'example.com', 'host', 'evil.com']\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { res.end() })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers\n    }, (err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'duplicate host header')\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/headers-crlf.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('CRLF Injection in Nodejs ‘undici’ via host', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const unsanitizedContentTypeInput = '12 \\r\\n\\r\\naaa:aaa'\n\n  try {\n    const { body } = await client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-type': 'application/json',\n        host: unsanitizedContentTypeInput\n      },\n      body: 'asd'\n    })\n    await body.dump()\n  } catch (err) {\n    t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n  }\n  await t.completed\n})\n"
  },
  {
    "path": "test/http-100.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst net = require('node:net')\nconst { once } = require('node:events')\n\ntest('ignore informational response', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    req.pipe(res)\n  })\n  after(() => server.close())\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'POST',\n    body: 'hello'\n  }, (err, response) => {\n    t.ifError(err)\n    const bufs = []\n    response.body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    response.body.on('end', () => {\n      t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n    })\n  })\n\n  await t.completed\n})\n\ntest('error 103 body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    socket.write('HTTP/1.1 103 Early Hints\\r\\n')\n    socket.write('Content-Length: 1\\r\\n')\n    socket.write('\\r\\n')\n    socket.write('a\\r\\n')\n  })\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n\n  after(() => server.close())\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    t.ok(true, 'pass')\n  })\n\n  t.rejects(client.request({\n    path: '/',\n    method: 'GET'\n  }), errors.HTTPParserError)\n\n  await t.completed\n})\n\ntest('error 100 body', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    socket.write('HTTP/1.1 100 Early Hints\\r\\n')\n    socket.write('\\r\\n')\n  })\n  after(() => server.close())\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err.message, 'bad response')\n  })\n  client.on('disconnect', () => {\n    t.ok(true, 'pass')\n  })\n  await t.completed\n})\n\ntest('error 101 upgrade', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (socket) => {\n    socket.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: example/1\\r\\nConnection: Upgrade\\r\\n')\n    socket.write('\\r\\n')\n  })\n  after(() => server.close())\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err.message, 'bad upgrade')\n  })\n  client.on('disconnect', () => {\n    t.ok(true, 'pass')\n  })\n  await t.completed\n})\n\ntest('1xx response without timeouts', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeProcessing()\n    setTimeout(() => req.pipe(res), 2000)\n  })\n  after(() => server.close())\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    bodyTimeout: 0,\n    headersTimeout: 0\n  })\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'POST',\n    body: 'hello'\n  }, (err, response) => {\n    t.ifError(err)\n    const bufs = []\n    response.body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    response.body.on('end', () => {\n      t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n    })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/http-req-destroy.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst undici = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { maybeWrapStream, consts } = require('./utils/async-iterators')\n\nfunction doNotKillReqSocket (bodyType) {\n  test(`do not kill req socket ${bodyType}`, async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const server1 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const client = new undici.Client(`http://localhost:${server2.address().port}`)\n      after(() => client.close())\n      client.request({\n        path: '/',\n        method: 'POST',\n        body: req\n      }, (err, response) => {\n        t.ifError(err)\n        setTimeout(() => {\n          response.body.on('data', buf => {\n            res.write(buf)\n            setTimeout(() => {\n              res.end()\n            }, 100)\n          })\n        }, 100)\n      })\n    })\n    after(() => server1.close())\n\n    const server2 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      setTimeout(() => {\n        req.pipe(res)\n      }, 100)\n    })\n    after(() => server2.close())\n\n    server1.listen(0, () => {\n      const client = new undici.Client(`http://localhost:${server1.address().port}`)\n      after(() => client.close())\n\n      const r = new Readable({ read () {} })\n      r.push('hello')\n      client.request({\n        path: '/',\n        method: 'POST',\n        body: maybeWrapStream(r, bodyType)\n      }, (err, response) => {\n        t.ifError(err)\n        const bufs = []\n        response.body.on('data', (buf) => {\n          bufs.push(buf)\n          r.push(null)\n        })\n        response.body.on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    })\n\n    server2.listen(0)\n\n    await t.completed\n  })\n}\n\ndoNotKillReqSocket(consts.STREAM)\ndoNotKillReqSocket(consts.ASYNC_ITERATOR)\n"
  },
  {
    "path": "test/http2-abort.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('#2364 - Concurrent aborts', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers, _flags, rawHeaders) => {\n    setTimeout(() => {\n      stream.respond({\n        'content-type': 'text/plain; charset=utf-8',\n        'x-custom-h2': 'hello',\n        ':status': 200\n      })\n      stream.end('hello h2!')\n    }, 100)\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  after(() => client.close())\n  const signal = AbortSignal.timeout(100)\n\n  client.request(\n    {\n      path: '/1',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    },\n    (err, response) => {\n      t.ifError(err)\n      t.strictEqual(\n        response.headers['content-type'],\n        'text/plain; charset=utf-8'\n      )\n      t.strictEqual(response.headers['x-custom-h2'], 'hello')\n      t.strictEqual(response.statusCode, 200)\n    }\n  )\n\n  client.request(\n    {\n      path: '/2',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      },\n      signal\n    },\n    (err, response) => {\n      t.strictEqual(err.name, 'TimeoutError')\n    }\n  )\n\n  client.request(\n    {\n      path: '/3',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    },\n    (err, response) => {\n      t.ifError(err)\n      t.strictEqual(\n        response.headers['content-type'],\n        'text/plain; charset=utf-8'\n      )\n      t.strictEqual(response.headers['x-custom-h2'], 'hello')\n      t.strictEqual(response.statusCode, 200)\n    }\n  )\n\n  client.request(\n    {\n      path: '/4',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      },\n      signal\n    },\n    (err, response) => {\n      t.strictEqual(err.name, 'TimeoutError')\n    }\n  )\n\n  await t.completed\n})\n\ntest('#2364 - Concurrent aborts (2nd variant)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  let counter = 0\n\n  server.on('stream', (stream, headers, _flags, rawHeaders) => {\n    counter++\n\n    if (counter % 2 === 0) {\n      setTimeout(() => {\n        if (stream.destroyed) {\n          return\n        }\n\n        stream.respond({\n          'content-type': 'text/plain; charset=utf-8',\n          'x-custom-h2': 'hello',\n          ':status': 200\n        })\n\n        stream.end('hello h2!')\n      }, 400)\n\n      return\n    }\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const signal = AbortSignal.timeout(300)\n\n  client.request(\n    {\n      path: '/1',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    },\n    (err, response) => {\n      t.ifError(err)\n      t.strictEqual(\n        response.headers['content-type'],\n        'text/plain; charset=utf-8'\n      )\n      t.strictEqual(response.headers['x-custom-h2'], 'hello')\n      t.strictEqual(response.statusCode, 200)\n    }\n  )\n\n  client.request(\n    {\n      path: '/2',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      },\n      signal\n    },\n    (err, response) => {\n      t.strictEqual(err.name, 'TimeoutError')\n    }\n  )\n\n  client.request(\n    {\n      path: '/3',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    },\n    (err, response) => {\n      t.ifError(err)\n      t.strictEqual(\n        response.headers['content-type'],\n        'text/plain; charset=utf-8'\n      )\n      t.strictEqual(response.headers['x-custom-h2'], 'hello')\n      t.strictEqual(response.statusCode, 200)\n    }\n  )\n\n  client.request(\n    {\n      path: '/4',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      },\n      signal\n    },\n    (err, response) => {\n      t.strictEqual(err.name, 'TimeoutError')\n    }\n  )\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-agent.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Agent } = require('..')\n\ntest('Agent should support H2 connection', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const body = []\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    t.strictEqual(headers['x-my-header'], 'foo')\n    t.strictEqual(headers[':method'], 'GET')\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Agent({\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    origin: `https://localhost:${server.address().port}`,\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-alpn.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst https = require('node:https')\nconst { once } = require('node:events')\nconst { createSecureServer } = require('node:http2')\nconst { readFileSync } = require('node:fs')\nconst { join } = require('node:path')\n\nconst { Client } = require('..')\n\n// get the crypto fixtures\nconst key = readFileSync(join(__dirname, 'fixtures', 'key.pem'), 'utf8')\nconst cert = readFileSync(join(__dirname, 'fixtures', 'cert.pem'), 'utf8')\nconst ca = readFileSync(join(__dirname, 'fixtures', 'ca.pem'), 'utf8')\n\ntest('Should upgrade to HTTP/2 when HTTPS/1 is available for GET', async (t) => {\n  t = tspl(t, { plan: 10 })\n\n  const body = []\n  const httpsBody = []\n\n  // create the server and server stream handler\n  const server = createSecureServer(\n    {\n      key,\n      cert,\n      allowHTTP1: true\n    },\n    (req, res) => {\n      const { socket: { alpnProtocol } } = req.httpVersion === '2.0' ? req.stream.session : req\n\n      // handle http/1 requests\n      res.writeHead(200, {\n        'content-type': 'application/json; charset=utf-8',\n        'x-custom-request-header': req.headers['x-custom-request-header'] || '',\n        'x-custom-response-header': `using ${req.httpVersion}`\n      })\n      res.end(JSON.stringify({\n        alpnProtocol,\n        httpVersion: req.httpVersion\n      }))\n    }\n  )\n\n  // close the server on teardown\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  // set the port\n  const port = server.address().port\n\n  // test undici against http/2\n  const client = new Client(`https://localhost:${port}`, {\n    connect: {\n      ca,\n      servername: 'agent1'\n    },\n    allowH2: true\n  })\n\n  // close the client on teardown\n  after(() => client.close())\n\n  // make an undici request using where it wants http/2\n  const response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-custom-request-header': 'want 2.0'\n    }\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-type'], 'application/json; charset=utf-8')\n  t.equal(response.headers['x-custom-request-header'], 'want 2.0')\n  t.equal(response.headers['x-custom-response-header'], 'using 2.0')\n  t.equal(Buffer.concat(body).toString('utf8'), JSON.stringify({\n    alpnProtocol: 'h2',\n    httpVersion: '2.0'\n  }))\n\n  // make an https request for http/1 to confirm undici is using http/2\n  const httpsOptions = {\n    ca,\n    servername: 'agent1',\n    headers: {\n      'x-custom-request-header': 'want 1.1'\n    }\n  }\n\n  const httpsResponse = await new Promise((resolve, reject) => {\n    const httpsRequest = https.get(`https://localhost:${port}/`, httpsOptions, (res) => {\n      res.on('data', (chunk) => {\n        httpsBody.push(chunk)\n      })\n\n      res.on('end', () => {\n        resolve(res)\n      })\n    }).on('error', (err) => {\n      reject(err)\n    })\n\n    after(() => httpsRequest.destroy())\n  })\n\n  t.equal(httpsResponse.statusCode, 200)\n  t.equal(httpsResponse.headers['content-type'], 'application/json; charset=utf-8')\n  t.equal(httpsResponse.headers['x-custom-request-header'], 'want 1.1')\n  t.equal(httpsResponse.headers['x-custom-response-header'], 'using 1.1')\n  t.equal(Buffer.concat(httpsBody).toString('utf8'), JSON.stringify({\n    alpnProtocol: false,\n    httpVersion: '1.1'\n  }))\n\n  await t.completed\n})\n\ntest('Should upgrade to HTTP/2 when HTTPS/1 is available for POST', async (t) => {\n  t = tspl(t, { plan: 15 })\n\n  const requestChunks = []\n  const responseBody = []\n\n  const httpsRequestChunks = []\n  const httpsResponseBody = []\n\n  const expectedBody = 'hello'\n  const buf = Buffer.from(expectedBody)\n  const body = new ArrayBuffer(buf.byteLength)\n\n  buf.copy(new Uint8Array(body))\n\n  // create the server and server stream handler\n  const server = createSecureServer(\n    {\n      key,\n      cert,\n      allowHTTP1: true\n    },\n    (req, res) => {\n      // use the stream handler for http2\n      if (req.httpVersion === '2.0') {\n        return\n      }\n\n      const { socket: { alpnProtocol } } = req\n\n      req.on('data', (chunk) => {\n        httpsRequestChunks.push(chunk)\n      })\n\n      req.on('end', () => {\n        // handle http/1 requests\n        res.writeHead(201, {\n          'content-type': 'text/plain; charset=utf-8',\n          'x-custom-request-header': req.headers['x-custom-request-header'] || '',\n          'x-custom-alpn-protocol': alpnProtocol\n        })\n        res.end('hello http/1!')\n      })\n    }\n  )\n\n  server.on('stream', (stream, headers) => {\n    t.equal(headers[':method'], 'POST')\n    t.equal(headers[':path'], '/')\n    t.equal(headers[':scheme'], 'https')\n\n    const { socket: { alpnProtocol } } = stream.session\n\n    stream.on('data', (chunk) => {\n      requestChunks.push(chunk)\n    })\n\n    stream.respond({\n      ':status': 201,\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-request-header': headers['x-custom-request-header'] || '',\n      'x-custom-alpn-protocol': alpnProtocol\n    })\n\n    stream.end('hello h2!')\n  })\n\n  // close the server on teardown\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  // set the port\n  const port = server.address().port\n\n  // test undici against http/2\n  const client = new Client(`https://localhost:${port}`, {\n    connect: {\n      ca,\n      servername: 'agent1'\n    },\n    allowH2: true\n  })\n\n  // close the client on teardown\n  after(() => client.close())\n\n  // make an undici request using where it wants http/2\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-custom-request-header': 'want 2.0'\n    },\n    body\n  })\n\n  response.body.on('data', (chunk) => {\n    responseBody.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.equal(response.statusCode, 201)\n  t.equal(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.equal(response.headers['x-custom-request-header'], 'want 2.0')\n  t.equal(response.headers['x-custom-alpn-protocol'], 'h2')\n  t.equal(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.equal(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  // make an https request for http/1 to confirm undici is using http/2\n  const httpsOptions = {\n    ca,\n    servername: 'agent1',\n    method: 'POST',\n    headers: {\n      'content-type': 'text/plain; charset=utf-8',\n      'content-length': Buffer.byteLength(body),\n      'x-custom-request-header': 'want 1.1'\n    }\n  }\n\n  const httpsResponse = await new Promise((resolve, reject) => {\n    const httpsRequest = https.request(`https://localhost:${port}/`, httpsOptions, (res) => {\n      res.on('data', (chunk) => {\n        httpsResponseBody.push(chunk)\n      })\n\n      res.on('end', () => {\n        resolve(res)\n      })\n    }).on('error', (err) => {\n      reject(err)\n    })\n\n    httpsRequest.on('error', (err) => {\n      reject(err)\n    })\n\n    httpsRequest.write(Buffer.from(body))\n\n    after(() => httpsRequest.destroy())\n  })\n\n  t.equal(httpsResponse.statusCode, 201)\n  t.equal(httpsResponse.headers['content-type'], 'text/plain; charset=utf-8')\n  t.equal(httpsResponse.headers['x-custom-request-header'], 'want 1.1')\n  t.equal(httpsResponse.headers['x-custom-alpn-protocol'], 'false')\n  t.equal(Buffer.concat(httpsResponseBody).toString('utf-8'), 'hello http/1!')\n  t.equal(Buffer.concat(httpsRequestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-body.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { createReadStream, readFileSync } = require('node:fs')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client, FormData, Response } = require('..')\n\ntest('Should handle h2 request without body', async t => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = ''\n  const requestChunks = []\n  const responseBody = []\n\n  server.on('stream', async (stream, headers) => {\n    t.strictEqual(headers[':method'], 'POST')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':scheme'], 'https')\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    for await (const chunk of stream) {\n      requestChunks.push(chunk)\n    }\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  for await (const chunk of response.body) {\n    responseBody.push(chunk)\n  }\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.strictEqual(requestChunks.length, 0)\n  t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n\ntest('Should handle h2 request with body (string or buffer) - dispatch', async t => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'hello from client!'\n  const response = []\n  const requestBody = []\n\n  server.on('stream', (stream, headers) => {\n    stream.on('data', chunk => requestBody.push(chunk))\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  client.dispatch(\n    {\n      path: '/',\n      method: 'POST',\n      headers: {\n        'x-my-header': 'foo',\n        'content-type': 'text/plain'\n      },\n      body: expectedBody\n    },\n    {\n      onConnect () {\n        t.ok(true, 'pass')\n      },\n      onError (err) {\n        t.ifError(err)\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n        t.strictEqual(headers[0].toString('utf-8'), 'content-type')\n        t.strictEqual(\n          headers[1].toString('utf-8'),\n          'text/plain; charset=utf-8'\n        )\n        t.strictEqual(headers[2].toString('utf-8'), 'x-custom-h2')\n        t.strictEqual(headers[3].toString('utf-8'), 'foo')\n      },\n      onData (chunk) {\n        response.push(chunk)\n      },\n      onBodySent (body) {\n        t.strictEqual(body.toString('utf-8'), expectedBody)\n      },\n      onComplete () {\n        t.strictEqual(Buffer.concat(response).toString('utf-8'), 'hello h2!')\n        t.strictEqual(\n          Buffer.concat(requestBody).toString('utf-8'),\n          'hello from client!'\n        )\n      }\n    }\n  )\n\n  await t.completed\n})\n\ntest('Should handle h2 request with body (stream)', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = readFileSync(__filename, 'utf-8')\n  const stream = createReadStream(__filename)\n  const requestChunks = []\n  const responseBody = []\n\n  server.on('stream', async (stream, headers) => {\n    t.strictEqual(headers[':method'], 'PUT')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':scheme'], 'https')\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    for await (const chunk of stream) {\n      requestChunks.push(chunk)\n    }\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'PUT',\n    headers: {\n      'x-my-header': 'foo'\n    },\n    body: stream\n  })\n\n  for await (const chunk of response.body) {\n    responseBody.push(chunk)\n  }\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n\ntest('Should handle h2 request with body (iterable)', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'hello'\n  const requestChunks = []\n  const responseBody = []\n  const iterableBody = {\n    [Symbol.iterator]: function * () {\n      const end = expectedBody.length - 1\n      for (let i = 0; i < end + 1; i++) {\n        yield expectedBody[i]\n      }\n\n      return expectedBody[end]\n    }\n  }\n\n  server.on('stream', (stream, headers) => {\n    t.strictEqual(headers[':method'], 'POST')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':scheme'], 'https')\n\n    stream.on('data', chunk => requestChunks.push(chunk))\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-my-header': 'foo'\n    },\n    body: iterableBody\n  })\n\n  response.body.on('data', chunk => {\n    responseBody.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n\ntest('Should handle h2 request with body (Blob)', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'asd'\n  const requestChunks = []\n  const responseBody = []\n  const body = new Blob(['asd'], {\n    type: 'application/json'\n  })\n\n  server.on('stream', (stream, headers) => {\n    t.strictEqual(headers[':method'], 'POST')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':scheme'], 'https')\n\n    stream.on('data', chunk => requestChunks.push(chunk))\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-my-header': 'foo'\n    },\n    body\n  })\n\n  response.body.on('data', chunk => {\n    responseBody.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n\ntest('Should handle h2 request with body (Blob:ArrayBuffer)',\n  async t => {\n    t = tspl(t, { plan: 8 })\n\n    const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n    const expectedBody = 'hello'\n    const requestChunks = []\n    const responseBody = []\n    const buf = Buffer.from(expectedBody)\n    const body = new ArrayBuffer(buf.byteLength)\n\n    buf.copy(new Uint8Array(body))\n\n    server.on('stream', (stream, headers) => {\n      t.strictEqual(headers[':method'], 'POST')\n      t.strictEqual(headers[':path'], '/')\n      t.strictEqual(headers[':scheme'], 'https')\n\n      stream.on('data', chunk => requestChunks.push(chunk))\n\n      stream.respond({\n        'content-type': 'text/plain; charset=utf-8',\n        'x-custom-h2': headers['x-my-header'],\n        ':status': 200\n      })\n\n      stream.end('hello h2!')\n    })\n\n    after(() => server.close())\n    await once(server.listen(0), 'listening')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n    after(() => client.close())\n\n    const response = await client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'x-my-header': 'foo'\n      },\n      body\n    })\n\n    response.body.on('data', chunk => {\n      responseBody.push(chunk)\n    })\n\n    await once(response.body, 'end')\n\n    t.strictEqual(response.statusCode, 200)\n    t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n    t.strictEqual(response.headers['x-custom-h2'], 'foo')\n    t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n    t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n    await t.completed\n  }\n)\n\ntest('#3803 - sending FormData bodies works', async (t) => {\n  const assert = tspl(t, { plan: 4 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  server.on('stream', async (stream, headers) => {\n    const contentLength = Number(headers['content-length'])\n\n    assert.ok(!Number.isNaN(contentLength))\n    assert.ok(headers['content-type']?.startsWith('multipart/form-data; boundary='))\n\n    stream.respond({ ':status': 200 })\n\n    const fd = await new Response(stream, {\n      headers: {\n        'content-type': headers['content-type']\n      }\n    }).formData()\n\n    assert.deepEqual(fd.get('a'), 'b')\n    assert.deepEqual(fd.get('c').name, 'e.fgh')\n\n    stream.end()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t.after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  const fd = new FormData()\n  fd.set('a', 'b')\n  fd.set('c', new Blob(['d']), 'e.fgh')\n\n  await client.request({\n    path: '/',\n    method: 'POST',\n    body: fd\n  })\n\n  await assert.completed\n})\n"
  },
  {
    "path": "test/http2-connection.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\nconst { Readable } = require('node:stream')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should support H2 connection', async t => {\n  t = tspl(t, { plan: 9 })\n\n  const body = []\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  let authority = ''\n\n  server.on('stream', (stream, headers, _flags, rawHeaders) => {\n    t.strictEqual(headers['x-my-header'], 'foo')\n    t.strictEqual(headers[':method'], 'GET')\n    t.strictEqual(headers[':scheme'], 'https')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':authority'], authority)\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n\n  await once(server.listen(0), 'listening')\n\n  authority = `localhost:${server.address().port}`\n  const client = new Client(`https://${authority}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  await t.completed\n})\n\ntest('Should support H2 connection(multiple requests)', async t => {\n  t = tspl(t, { plan: 21 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', async (stream, headers, _flags, rawHeaders) => {\n    t.strictEqual(headers['x-my-header'], 'foo')\n    t.strictEqual(headers[':method'], 'POST')\n    const reqData = []\n    stream.on('data', chunk => reqData.push(chunk.toString()))\n    await once(stream, 'end')\n    const reqBody = reqData.join('')\n    t.strictEqual(reqBody.length > 0, true)\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end(`hello h2! ${reqBody}`)\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  for (let i = 0; i < 3; i++) {\n    const sendBody = `seq ${i}`\n    const body = []\n    const response = await client.request({\n      path: '/',\n      method: 'POST',\n      headers: {\n        'content-type': 'text/plain; charset=utf-8',\n        'x-my-header': 'foo'\n      },\n      body: Readable.from(sendBody)\n    })\n\n    response.body.on('data', chunk => {\n      body.push(chunk)\n    })\n\n    await once(response.body, 'end')\n\n    t.strictEqual(response.statusCode, 200)\n    t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n    t.strictEqual(response.headers['x-custom-h2'], 'hello')\n    t.strictEqual(Buffer.concat(body).toString('utf8'), `hello h2! ${sendBody}`)\n  }\n\n  await t.completed\n})\n\ntest('Should support H2 connection (headers as array)', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const body = []\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    t.strictEqual(headers['x-my-header'], 'foo, bar')\n    t.strictEqual(headers['x-my-drink'], 'coffee, tea, water')\n    t.strictEqual(headers['x-other'], 'value')\n    t.strictEqual(headers[':method'], 'GET')\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: [\n      'x-my-header', 'foo',\n      'x-my-drink', ['coffee', 'tea'],\n      'x-my-drink', 'water',\n      'X-My-Header', 'bar',\n      'x-other', 'value'\n    ]\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  await t.completed\n})\n\ntest('Should support multiple header values with semicolon separator', async t => {\n  t = tspl(t, { plan: 9 * 2 })\n\n  const body = []\n  const body2 = []\n  const expectedCookieHeaders = ['a=b', 'c=d', 'e=f']\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    t.strictEqual(headers['x-my-header'], 'foo, bar')\n    t.strictEqual(headers['x-my-drink'], 'coffee, tea, water')\n    t.strictEqual(headers['x-other'], 'value')\n    t.strictEqual(headers['cookie'], expectedCookieHeaders.join('; '))\n    t.strictEqual(headers[':method'], 'GET')\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: [\n      'x-my-header', 'foo',\n      'x-my-drink', ['coffee', 'tea'],\n      'x-my-drink', 'water',\n      'X-My-Header', 'bar',\n      'x-other', 'value',\n      'cookie', expectedCookieHeaders\n    ]\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  const response2 = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: [\n      'x-my-header', 'foo',\n      'x-my-drink', ['coffee', 'tea'],\n      'cookie', 'a=b',\n      'x-my-drink', 'water',\n      'X-My-Header', 'bar',\n      'cookie', 'c=d',\n      'x-other', 'value',\n      'cookie', 'e=f'\n    ]\n  })\n\n  response2.body.on('data', chunk => {\n    body2.push(chunk)\n  })\n\n  await once(response2.body, 'end')\n\n  t.strictEqual(response2.statusCode, 200)\n  t.strictEqual(response2.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response2.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  await t.completed\n})\n\ntest('Should support H2 connection(POST Buffer)', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createSecureServer({ ...await pem.generate({ opts: { keySize: 2048 } }), allowHTTP1: false })\n\n  server.on('stream', async (stream, headers, _flags, rawHeaders) => {\n    t.strictEqual(headers[':method'], 'POST')\n    const reqData = []\n    stream.on('data', chunk => reqData.push(chunk.toString()))\n    await once(stream, 'end')\n    t.strictEqual(reqData.join(''), 'hello!')\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const sendBody = 'hello!'\n  const body = []\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    body: sendBody\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-continue.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should handle h2 continue', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const requestBody = []\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }), () => {})\n  const responseBody = []\n\n  server.on('checkContinue', (request, response) => {\n    t.strictEqual(request.headers.expect, '100-continue')\n    t.strictEqual(request.headers['x-my-header'], 'foo')\n    t.strictEqual(request.headers[':method'], 'POST')\n    response.writeContinue()\n\n    request.on('data', chunk => requestBody.push(chunk))\n\n    response.writeHead(200, {\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'foo'\n    })\n    response.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    expectContinue: true,\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-my-header': 'foo'\n    },\n    expectContinue: true\n  })\n\n  response.body.on('data', chunk => {\n    responseBody.push(chunk)\n  })\n\n  await once(response.body, 'end')\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-dispatcher.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\nconst { setTimeout: sleep } = require('node:timers/promises')\nconst { Writable, pipeline, PassThrough, Readable } = require('node:stream')\n\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Dispatcher#Stream', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'hello from client!'\n  const bufs = []\n  let requestBody = ''\n\n  server.on('stream', (stream, headers) => {\n    stream.setEncoding('utf-8')\n    stream.on('data', chunk => {\n      requestBody += chunk\n    })\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    stream.respond({ ':status': 200, 'x-custom': 'custom-header' })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  await client.stream(\n    { path: '/', opaque: { bufs }, method: 'POST', body: expectedBody },\n    ({ statusCode, headers, opaque: { bufs } }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['x-custom'], 'custom-header')\n\n      return new Writable({\n        write (chunk, _encoding, cb) {\n          bufs.push(chunk)\n          cb()\n        }\n      })\n    }\n  )\n\n  t.strictEqual(Buffer.concat(bufs).toString('utf-8'), 'hello h2!')\n  t.strictEqual(requestBody, expectedBody)\n\n  await t.completed\n})\n\ntest('Dispatcher#Pipeline', async t => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = 'hello from client!'\n  const bufs = []\n  let requestBody = ''\n\n  server.on('stream', (stream, headers) => {\n    stream.setEncoding('utf-8')\n    stream.on('data', chunk => {\n      requestBody += chunk\n    })\n\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    stream.respond({ ':status': 200, 'x-custom': 'custom-header' })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  pipeline(\n    new Readable({\n      read () {\n        this.push(Buffer.from(expectedBody))\n        this.push(null)\n      }\n    }),\n    client.pipeline(\n      { path: '/', method: 'POST', body: expectedBody },\n      ({ statusCode, headers, body }) => {\n        t.strictEqual(statusCode, 200)\n        t.strictEqual(headers['x-custom'], 'custom-header')\n\n        return pipeline(body, new PassThrough(), () => {})\n      }\n    ),\n    new Writable({\n      write (chunk, _, cb) {\n        bufs.push(chunk)\n        cb()\n      }\n    }),\n    err => {\n      t.ifError(err)\n      t.strictEqual(Buffer.concat(bufs).toString('utf-8'), 'hello h2!')\n      t.strictEqual(requestBody, expectedBody)\n    }\n  )\n\n  await t.completed\n})\n\ntest('Dispatcher#Connect', async t => {\n  t = tspl(t, { plan: 5 })\n\n  const proxy = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  const expectedBody = 'hello from client!'\n  let responseBody = ''\n  let requestBody = ''\n\n  proxy.on('stream', async (stream, headers) => {\n    if (headers[':method'] !== 'CONNECT') {\n      t.fail('Unexpected CONNECT method')\n      return\n    }\n\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    const forward = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n    after(() => forward.close())\n\n    try {\n      const response = await forward.request({\n        path: '/',\n        method: 'POST',\n        body: stream,\n        headers: {\n          'x-my-header': headers['x-my-header']\n        }\n      })\n\n      stream.respond({ ':status': 200, 'x-my-header': response.headers['x-my-header'] })\n      response.body.pipe(stream)\n    } catch (err) {\n      stream.destroy(err)\n    }\n  })\n\n  server.on('stream', (stream, headers) => {\n    stream.setEncoding('utf-8')\n    stream.on('data', chunk => {\n      requestBody += chunk\n    })\n    stream.once('end', () => {\n      t.strictEqual(requestBody, expectedBody)\n    })\n\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    stream.respond({ ':status': 200, 'x-my-header': headers['x-my-header'] })\n    stream.end('helloworld')\n  })\n\n  await once(proxy.listen(0), 'listening')\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${proxy.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n  after(() => proxy.close())\n  after(() => server.close())\n\n  const { statusCode, headers, socket } = await client.connect({ path: '/', headers: { 'x-my-header': 'foo' } })\n  t.strictEqual(statusCode, 200)\n  t.strictEqual(headers['x-my-header'], 'foo')\n  t.strictEqual(socket.closed, false)\n\n  socket.on('data', chunk => { responseBody += chunk })\n  socket.once('end', () => {\n    t.strictEqual(responseBody, 'helloworld')\n  })\n  socket.setEncoding('utf-8')\n  socket.write(expectedBody)\n  socket.end()\n\n  await t.completed\n})\n\ntest('Dispatcher#Upgrade - Should throw on non-websocket upgrade', async t => {\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', async (stream, headers) => {\n    stream.end()\n  })\n\n  t = tspl(t, { plan: 1 })\n\n  server.listen(0, async () => {\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    after(() => server.close())\n    after(() => client.close())\n\n    try {\n      await client.upgrade({ path: '/', protocol: 'any' })\n    } catch (error) {\n      t.strictEqual(error.message, 'Custom upgrade \"any\" not supported over HTTP/2')\n    }\n  })\n\n  await t.completed\n})\n\ntest('Dispatcher#Upgrade', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createSecureServer({ ...(await pem.generate({ opts: { keySize: 2048 } })), settings: { enableConnectProtocol: true } })\n\n  server.on('stream', (stream, headers) => {\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    stream.respond({ ':status': 200 })\n    stream.resume()\n\n    stream.end()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close().then(() => { server.close() }))\n\n  const { socket } = await client.upgrade({ path: '/', protocol: 'websocket' })\n\n  t.ok(socket.readable)\n  t.ok(socket.writable)\n  t.strictEqual(socket.closed, false)\n\n  after(() => socket.end())\n\n  await t.completed\n})\n\ntest('Dispatcher#destroy', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const promises = []\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    stream.on('error', err => {\n      t.fail(err)\n    })\n    stream.resume()\n    setTimeout(stream.end.bind(stream), 1500)\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  // we don't want to close the client gracefully in an after hook\n\n  promises.push(\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    })\n  )\n\n  promises.push(\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    })\n  )\n\n  promises.push(\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    })\n  )\n\n  promises.push(\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    })\n  )\n\n  await client.destroy()\n\n  const results = await Promise.allSettled(promises)\n\n  t.strictEqual(results[0].status, 'rejected')\n  t.strictEqual(results[1].status, 'rejected')\n  t.strictEqual(results[2].status, 'rejected')\n  t.strictEqual(results[3].status, 'rejected')\n\n  await t.completed\n})\n\ntest('Should handle h2 request without body', async t => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const expectedBody = ''\n  const requestChunks = []\n  const responseBody = []\n\n  server.on('stream', async (stream, headers) => {\n    t.strictEqual(headers[':method'], 'POST')\n    t.strictEqual(headers[':path'], '/')\n    t.strictEqual(headers[':scheme'], 'https')\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    stream.on('error', err => {\n      t.fail(err)\n    })\n\n    for await (const chunk of stream) {\n      requestChunks.push(chunk)\n    }\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'POST',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  for await (const chunk of response.body) {\n    responseBody.push(chunk)\n  }\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'foo')\n  t.strictEqual(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')\n  t.strictEqual(requestChunks.length, 0)\n  t.strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)\n\n  await t.completed\n})\n\ntest('Should only accept valid ping interval values', async t => {\n  const planner = tspl(t, { plan: 3 })\n\n  planner.throws(() => new Client('https://localhost', {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: -1\n  }))\n\n  planner.throws(() => new Client('https://localhost', {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: 'foo'\n  }))\n\n  planner.throws(() => new Client('https://localhost', {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: 1.1\n  }))\n\n  await planner.completed\n})\n\ntest('Should send http2 PING frames', async t => {\n  const server = createSecureServer(pem)\n  let session = null\n  let pingCounter = 0\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    },\n    {\n      waitForTrailers: true\n    })\n\n    stream.on('wantTrailers', () => {\n      stream.sendTrailers({\n        'x-trailer': 'hello'\n      })\n    })\n\n    stream.end('hello h2!')\n  })\n\n  server.on('session', (s) => {\n    session = s\n    session.on('ping', (payload) => {\n      pingCounter++\n    })\n  })\n\n  t = tspl(t, { plan: 2 })\n\n  server.listen(0, '127.0.0.1')\n  await once(server, 'listening')\n\n  const client = new Client(`https://${server.address().address}:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: 100\n  })\n\n  after(async () => {\n    server.close()\n  })\n\n  client.dispatch({\n    path: '/',\n    method: 'PUT',\n    body: 'hello'\n  }, {\n    onConnect () {\n\n    },\n    onHeaders () {\n      return true\n    },\n    onData () {\n      return true\n    },\n    onComplete (trailers) {\n      t.strictEqual(trailers['x-trailer'], 'hello')\n    },\n    onError (err) {\n      t.ifError(err)\n    }\n  })\n\n  await sleep(600)\n  await client.close()\n  t.equal(pingCounter, 5, 'Expected 5 PING frames to be sent')\n\n  await t.completed\n})\n\ntest('Should not send http2 PING frames if interval === 0', async t => {\n  const server = createSecureServer(pem)\n  let session = null\n  let pingCounter = 0\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    },\n    {\n      waitForTrailers: true\n    })\n\n    stream.on('wantTrailers', () => {\n      stream.sendTrailers({\n        'x-trailer': 'hello'\n      })\n    })\n\n    stream.end('hello h2!')\n  })\n\n  server.on('session', (s) => {\n    session = s\n    session.on('ping', (payload) => {\n      pingCounter++\n    })\n  })\n\n  t = tspl(t, { plan: 2 })\n\n  server.listen(0, '127.0.0.1')\n  await once(server, 'listening')\n\n  const client = new Client(`https://${server.address().address}:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: 0\n  })\n\n  after(async () => {\n    server.close()\n  })\n\n  client.dispatch({\n    path: '/',\n    method: 'PUT',\n    body: 'hello'\n  }, {\n    onConnect () {\n\n    },\n    onHeaders () {\n      return true\n    },\n    onData () {\n      return true\n    },\n    onComplete (trailers) {\n      t.strictEqual(trailers['x-trailer'], 'hello')\n    },\n    onError (err) {\n      t.ifError(err)\n    }\n  })\n\n  await sleep(500)\n  await client.close()\n  t.equal(pingCounter, 0, 'Expected 0 PING frames to be sent')\n\n  await t.completed\n})\n\ntest('Should not send http2 PING frames after connection is closed', async t => {\n  const server = createSecureServer(pem)\n  let session = null\n  let pingCounter = 0\n\n  server.on('stream', async (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    },\n    {\n      waitForTrailers: true\n    })\n\n    stream.on('wantTrailers', () => {\n      stream.sendTrailers({\n        'x-trailer': 'hello'\n      })\n    })\n\n    stream.end('hello h2!')\n  })\n\n  server.on('session', (s) => {\n    session = s\n    session.on('ping', (payload) => {\n      pingCounter++\n    })\n  })\n\n  t = tspl(t, { plan: 2 })\n\n  server.listen(0, '127.0.0.1')\n  await once(server, 'listening')\n\n  const client = new Client(`https://${server.address().address}:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    pingInterval: 0\n  })\n\n  after(async () => {\n    session.close()\n    server.close()\n  })\n\n  client.dispatch({\n    path: '/',\n    method: 'PUT',\n    body: 'hello'\n  }, {\n    onConnect () {\n\n    },\n    onHeaders () {\n      return true\n    },\n    onData () {\n      return true\n    },\n    onComplete (trailers) {\n      t.strictEqual(trailers['x-trailer'], 'hello')\n    },\n    onError (err) {\n      t.ifError(err)\n    }\n  })\n\n  await client.close()\n  await sleep(500)\n  t.equal(pingCounter, 0, 'Expected 0 PING frames to be sent')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-goaway.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('#3046 - GOAWAY Frame', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    setTimeout(() => {\n      if (stream.closed) return\n      stream.end('Hello World')\n    }, 100)\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n  })\n\n  server.on('session', session => {\n    setTimeout(() => {\n      session.goaway()\n    }, 50)\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  client.on('disconnect', (url, disconnectClient, err) => {\n    t.ok(url instanceof URL)\n    t.deepStrictEqual(disconnectClient, [client])\n    t.strictEqual(err.message, 'HTTP/2: \"GOAWAY\" frame received with code 0')\n  })\n\n  client.on('connectionError', (url, disconnectClient, err) => {\n    t.ok(url instanceof URL)\n    t.deepStrictEqual(disconnectClient, [client])\n    t.strictEqual(err.message, 'HTTP/2: \"GOAWAY\" frame received with code 0')\n  })\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(response.statusCode, 200)\n\n  await t.rejects(response.body.text(), {\n    message: 'HTTP/2: \"GOAWAY\" frame received with code 0',\n    code: 'UND_ERR_SOCKET'\n  })\n\n  await t.completed\n})\n\ntest('#3753 - Handle GOAWAY Gracefully', async (t) => {\n  t = tspl(t, { plan: 30 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  let counter = 0\n  let session = null\n\n  server.on('session', s => {\n    session = s\n  })\n\n  server.on('stream', (stream) => {\n    counter++\n\n    // Due to the nature of the test, we need to ignore the error\n    // that is thrown when the session is destroyed and stream\n    // is in-flight\n    stream.on('error', () => {})\n    if (counter === 9 && session != null) {\n      session.goaway()\n      stream.end()\n    } else {\n      stream.respond({\n        'content-type': 'text/plain',\n        ':status': 200\n      })\n      setTimeout(() => {\n        stream.end('hello world')\n      }, 150)\n    }\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    pipelining: 2,\n    allowH2: true\n  })\n  after(() => client.close())\n\n  for (let i = 0; i < 15; i++) {\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    }, (err, response) => {\n      if (err) {\n        t.strictEqual(err.message, 'HTTP/2: \"GOAWAY\" frame received with code 0')\n        t.strictEqual(err.code, 'UND_ERR_SOCKET')\n      } else {\n        t.strictEqual(response.statusCode, 200)\n        ;(async function () {\n          let body\n          try {\n            body = await response.body.text()\n          } catch (err) {\n            t.strictEqual(err.code, 'UND_ERR_SOCKET')\n            return\n          }\n          t.strictEqual(body, 'hello world')\n        })()\n      }\n    })\n  }\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-instantiation.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should throw if bad allowH2 has been passed', async t => {\n  t = tspl(t, { plan: 1 })\n\n  t.throws(() => {\n    // eslint-disable-next-line\n    new Client('https://localhost:1000', {\n      allowH2: 'true'\n    })\n  }, {\n    message: 'allowH2 must be a valid boolean value'\n  })\n\n  await t.completed\n})\n\ntest('Should throw if bad maxConcurrentStreams has been passed', async t => {\n  t = tspl(t, { plan: 2 })\n\n  t.throws(() => {\n    // eslint-disable-next-line\n    new Client('https://localhost:1000', {\n      allowH2: true,\n      maxConcurrentStreams: {}\n    })\n  }, {\n    message: 'maxConcurrentStreams must be a positive integer, greater than 0'\n  })\n\n  t.throws(() => {\n    // eslint-disable-next-line\n    new Client('https://localhost:1000', {\n      allowH2: true,\n      maxConcurrentStreams: -1\n    })\n  }, {\n    message: 'maxConcurrentStreams must be a positive integer, greater than 0'\n  })\n\n  await t.completed\n})\n\ntest(\n  'Request should fail if allowH2 is false and server advertises h1 only',\n  async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createSecureServer(\n      {\n        ...await pem.generate({ opts: { keySize: 2048 } }),\n        allowHTTP1: false,\n        ALPNProtocols: ['http/1.1']\n      },\n      (req, res) => {\n        t.fail('Should not create a valid h2 stream')\n      }\n    )\n\n    after(() => server.close())\n    await once(server.listen(0), 'listening')\n\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      allowH2: false,\n      connect: {\n        rejectUnauthorized: false\n      }\n    })\n    after(() => client.close())\n\n    await t.rejects(client.request({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'x-my-header': 'foo'\n      }\n    }))\n\n    await t.completed\n  })\n"
  },
  {
    "path": "test/http2-late-data.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { EventEmitter } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst connectH2 = require('../lib/dispatcher/client-h2')\nconst Request = require('../lib/core/request')\nconst {\n  kUrl,\n  kSocket,\n  kMaxConcurrentStreams,\n  kHTTP2InitialWindowSize,\n  kHTTP2ConnectionWindowSize,\n  kBodyTimeout,\n  kStrictContentLength,\n  kQueue,\n  kRunningIdx,\n  kPendingIdx,\n  kOnError,\n  kResume,\n  kRunning,\n  kPingInterval\n} = require('../lib/core/symbols')\n\ntest('Should ignore late http2 data after request completion', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const http2 = require('node:http2')\n  const originalConnect = http2.connect\n\n  class FakeSocket extends EventEmitter {\n    constructor () {\n      super()\n      this.destroyed = false\n    }\n\n    destroy () {\n      this.destroyed = true\n      return this\n    }\n\n    ref () {}\n    unref () {}\n  }\n\n  class FakeStream extends EventEmitter {\n    setTimeout () {}\n    pause () {}\n    resume () {}\n    close () {}\n    write () { return true }\n    end () {}\n    cork () {}\n    uncork () {}\n  }\n\n  class FakeSession extends EventEmitter {\n    constructor (stream) {\n      super()\n      this.stream = stream\n      this.closed = false\n      this.destroyed = false\n    }\n\n    request () {\n      return this.stream\n    }\n\n    close () {\n      this.closed = true\n    }\n\n    destroy () {\n      this.destroyed = true\n    }\n\n    ref () {}\n    unref () {}\n    ping (_, cb) {\n      cb(null, 0)\n    }\n  }\n\n  const stream = new FakeStream()\n  const session = new FakeSession(stream)\n\n  http2.connect = function connectStub () {\n    return session\n  }\n\n  after(() => {\n    http2.connect = originalConnect\n  })\n\n  let resumeCalls = 0\n  let onDataCalls = 0\n  let onCompleteCalls = 0\n\n  const client = {\n    [kUrl]: new URL('https://localhost'),\n    [kSocket]: null,\n    [kMaxConcurrentStreams]: 100,\n    [kHTTP2InitialWindowSize]: null,\n    [kHTTP2ConnectionWindowSize]: null,\n    [kBodyTimeout]: 30_000,\n    [kStrictContentLength]: true,\n    [kQueue]: [],\n    [kRunningIdx]: 0,\n    [kPendingIdx]: 0,\n    [kRunning]: 1,\n    [kPingInterval]: 0,\n    [kOnError] (err) {\n      t.ifError(err)\n    },\n    [kResume] () {\n      resumeCalls++\n    },\n    emit () {},\n    destroyed: false\n  }\n\n  const context = connectH2(client, new FakeSocket())\n\n  const request = new Request('https://localhost', {\n    path: '/',\n    method: 'GET',\n    headers: {}\n  }, {\n    onConnect () {},\n    onHeaders () {\n      return true\n    },\n    onData () {\n      onDataCalls++\n      return true\n    },\n    onComplete (trailers) {\n      onCompleteCalls++\n      t.strictEqual(trailers['x-trailer'], 'hello')\n    },\n    onError (err) {\n      t.ifError(err)\n    }\n  })\n\n  client[kQueue].push(request)\n\n  t.ok(context.write(request))\n\n  stream.emit('response', { ':status': 200 })\n  stream.emit('trailers', { 'x-trailer': 'hello' })\n\n  t.doesNotThrow(() => {\n    stream.emit('data', Buffer.from('late-data'))\n  })\n\n  stream.emit('end')\n\n  t.strictEqual(onCompleteCalls, 1)\n  t.strictEqual(onDataCalls, 0)\n  t.ok(resumeCalls >= 1)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-pseudo-headers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should provide pseudo-headers in proper order', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  server.on('stream', (stream, _headers, _flags, rawHeaders) => {\n    t.deepStrictEqual(rawHeaders, [\n      ':authority',\n      `localhost:${server.address().port}`,\n      ':method',\n      'GET',\n      ':path',\n      '/',\n      ':scheme',\n      'https'\n    ])\n\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      ':status': 200\n    })\n    stream.end()\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n\n  t.strictEqual(response.statusCode, 200)\n\n  await response.body.dump()\n\n  await t.completed\n})\n\ntest('The h2 pseudo-headers is not included in the headers', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    stream.respond({\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  const response = await client.request({\n    path: '/',\n    method: 'GET'\n  })\n\n  await response.body.text()\n\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers[':status'], undefined)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-resume-null-request.js",
    "content": "'use strict'\n\n// Regression test for:\n// TypeError: Cannot read properties of null (reading 'servername')\n//   at _resume (lib/dispatcher/client.js)\n//\n// Race condition in H2: when a stream's 'end' event fires, client-h2.js does:\n//   client[kQueue][client[kRunningIdx]++] = null   <- nulls the slot\n//   client[kResume]()                              <- _resume reads null slot\n//\n// If kPendingIdx was reset to kRunningIdx (e.g. by onHttp2SocketClose) between\n// writeH2 dispatching the stream and the 'end' event firing, kPendingIdx now\n// points at the null slot. _resume fetches kQueue[kPendingIdx] = null and\n// crashes on null.servername.\n//\n// Fix: null guard in _resume after fetching the request from the queue.\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { Client } = require('..')\nconst {\n  kQueue,\n  kRunningIdx,\n  kPendingIdx,\n  kResume\n} = require('../lib/core/symbols')\n\ntest('_resume should not crash when kQueue[kPendingIdx] is null', () => {\n  // Create a client against a non-existent server — we never connect,\n  // we only need the properly-initialized internal state.\n  const client = new Client('https://localhost:1', {\n    connect: { rejectUnauthorized: false },\n    allowH2: true\n  })\n\n  // Reproduce the exact queue state that triggers the bug:\n  //\n  //   kQueue = [null]   (slot was nulled by: kQueue[kRunningIdx++] = null)\n  //   kRunningIdx = 0   (points at the null slot)\n  //   kPendingIdx = 0   (reset to kRunningIdx by onHttp2SocketClose)\n  //\n  // kPending = kQueue.length - kPendingIdx = 1 - 0 = 1  (non-zero, passes the guard)\n  // kRunning = kPendingIdx - kRunningIdx   = 0 - 0 = 0  (below pipelining limit)\n  // kQueue[kPendingIdx] = null                           (the crash point)\n  client[kQueue].push(null)\n  client[kRunningIdx] = 0\n  client[kPendingIdx] = 0\n\n  // Calling kResume() now replicates what client-h2.js does after nulling the slot.\n  // Without the fix: TypeError: Cannot read properties of null (reading 'servername')\n  // With the fix:    returns early safely.\n  assert.doesNotThrow(\n    () => client[kResume](),\n    'Expected _resume to handle null queue slot without throwing'\n  )\n\n  // Restore a valid queue state before destroying so the client\n  // doesn't trip over the null slot we injected during cleanup.\n  client[kQueue].length = 0\n  client[kRunningIdx] = 0\n  client[kPendingIdx] = 0\n\n  client.destroy().catch(() => {})\n})\n"
  },
  {
    "path": "test/http2-stream.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should throw informational error on half-closed streams (remote)', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    stream.destroy()\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  await t.rejects(client\n    .request({\n      path: '/',\n      method: 'GET'\n    }), {\n    message: 'HTTP/2: stream half-closed (remote)',\n    code: 'UND_ERR_INFO'\n  })\n  await t.rejects(client\n    .request({\n      path: '/',\n      method: 'GET'\n    }), {\n    message: 'HTTP/2: stream half-closed (remote)',\n    code: 'UND_ERR_INFO'\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-timeout.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { createReadStream } = require('node:fs')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should handle http2 stream timeout', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n  const stream = createReadStream(__filename)\n\n  server.on('stream', (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    })\n\n    setTimeout(() => {\n      stream.end('hello h2!')\n    }, 500)\n  })\n\n  after(() => server.close())\n  await once(server.listen(0), 'listening')\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true,\n    bodyTimeout: 50\n  })\n  after(() => client.close())\n\n  const res = await client.request({\n    path: '/',\n    method: 'PUT',\n    headers: {\n      'x-my-header': 'foo'\n    },\n    body: stream\n  })\n\n  await t.rejects(res.body.text(), {\n    message: 'HTTP/2: \"stream timeout after 50\"'\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-trailers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst { once } = require('node:events')\n\nconst pem = require('@metcoder95/https-pem')\n\nconst { Client } = require('..')\n\ntest('Should handle http2 trailers', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))\n\n  server.on('stream', (stream, headers) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': headers['x-my-header'],\n      ':status': 200\n    },\n    {\n      waitForTrailers: true\n    })\n\n    stream.on('wantTrailers', () => {\n      stream.sendTrailers({\n        'x-trailer': 'hello'\n      })\n    })\n\n    stream.end('hello h2!')\n  })\n\n  after(() => server.close())\n  await once(server.listen(0, '127.0.0.1'), 'listening')\n\n  const client = new Client(`https://${server.address().address}:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n  after(() => client.close())\n\n  client.dispatch({\n    path: '/',\n    method: 'PUT',\n    body: 'hello'\n  }, {\n    onConnect () {\n\n    },\n    onHeaders () {\n      return true\n    },\n    onData () {\n      return true\n    },\n    onComplete (trailers) {\n      t.strictEqual(trailers['x-trailer'], 'hello')\n    },\n    onError (err) {\n      t.ifError(err)\n    }\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/http2-window-size.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { EventEmitter } = require('node:events')\nconst connectH2 = require('../lib/dispatcher/client-h2')\nconst {\n  kUrl,\n  kSocket,\n  kMaxConcurrentStreams,\n  kHTTP2Session,\n  kHTTP2InitialWindowSize,\n  kHTTP2ConnectionWindowSize\n} = require('../lib/core/symbols')\n\ntest('Should plumb initialWindowSize and connectionWindowSize into the HTTP/2 session creation path', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const http2 = require('node:http2')\n  const originalConnect = http2.connect\n\n  /** @type {any} */\n  let seenConnectOptions = null\n  /** @type {number[]} */\n  const setLocalWindowSizeCalls = []\n\n  class FakeSession extends EventEmitter {\n    unref () {}\n    ref () {}\n    close () {}\n    destroy () {}\n    request () {\n      throw new Error('not implemented')\n    }\n\n    setLocalWindowSize (size) {\n      setLocalWindowSizeCalls.push(size)\n    }\n  }\n\n  class FakeSocket extends EventEmitter {\n    constructor () {\n      super()\n      this.destroyed = false\n    }\n\n    unref () {}\n    ref () {}\n    destroy () {\n      this.destroyed = true\n      return this\n    }\n  }\n\n  const fakeSession = new FakeSession()\n\n  http2.connect = function connectStub (_authority, options) {\n    seenConnectOptions = options\n    return fakeSession\n  }\n\n  after(() => {\n    http2.connect = originalConnect\n  })\n\n  const initialWindowSize = 12345\n  const connectionWindowSize = 77777\n\n  const client = {\n    [kUrl]: new URL('https://localhost'),\n    [kMaxConcurrentStreams]: 100,\n    [kHTTP2InitialWindowSize]: initialWindowSize,\n    [kHTTP2ConnectionWindowSize]: connectionWindowSize,\n    [kSocket]: null,\n    [kHTTP2Session]: null\n  }\n\n  const socket = new FakeSocket()\n\n  connectH2(client, socket)\n\n  t.ok(seenConnectOptions && seenConnectOptions.settings)\n  t.strictEqual(seenConnectOptions.settings.enablePush, false)\n  t.strictEqual(\n    seenConnectOptions.settings.initialWindowSize,\n    initialWindowSize\n  )\n  t.strictEqual(client[kHTTP2Session], fakeSession)\n\n  // Emit 'connect' event\n  process.nextTick(() => {\n    fakeSession.emit('connect')\n  })\n\n  await new Promise((resolve) => process.nextTick(resolve))\n\n  t.strictEqual(setLocalWindowSizeCalls.length, 1)\n  t.strictEqual(setLocalWindowSizeCalls[0], connectionWindowSize)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/https.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:https')\nconst pem = require('@metcoder95/https-pem')\n\ntest('https get with tls opts', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      tls: {\n        rejectUnauthorized: false\n      }\n    })\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('https get with tls opts ip', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`https://127.0.0.1:${server.address().port}`, {\n      tls: {\n        rejectUnauthorized: false\n      }\n    })\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/imports/undici-import.ts",
    "content": "import { expectType } from 'tsd'\nimport { Dispatcher, interceptors, request } from '../../'\n\nasync function exampleCode () {\n  const retry = interceptors.retry()\n  const rd = interceptors.redirect()\n  const dump = interceptors.dump()\n\n  expectType<Dispatcher.DispatcherComposeInterceptor>(retry)\n  expectType<Dispatcher.DispatcherComposeInterceptor>(rd)\n  expectType<Dispatcher.DispatcherComposeInterceptor>(dump)\n\n  await request('http://localhost:3000/foo')\n}\n\nexampleCode()\n"
  },
  {
    "path": "test/inflight-and-close.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { request } = require('..')\nconst http = require('node:http')\n\ntest('inflight and close', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200)\n    res.end('Response body')\n    res.socket.end() // Close the connection immediately with every response\n  }).listen(0, '127.0.0.1', function () {\n    const url = `http://127.0.0.1:${this.address().port}`\n    request(url)\n      .then(({ statusCode, headers, body }) => {\n        t.ok(true, 'first response')\n        body.resume()\n        body.on('close', function () {\n          t.ok(true, 'first body closed')\n        })\n        return request(url)\n          .then(({ statusCode, headers, body }) => {\n            t.ok(true, 'second response')\n            body.resume()\n            body.on('close', function () {\n              server.close()\n            })\n          })\n      }).catch((err) => {\n        t.ifError(err)\n      })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/infra/collect-a-sequence-of-code-points.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\n\nconst { collectASequenceOfCodePoints } = require('../../lib/web/infra')\n\ntest('https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points', (t) => {\n  const input = 'text/plain;base64,'\n  const position = { position: 0 }\n  const result = collectASequenceOfCodePoints(\n    (char) => char !== ';',\n    input,\n    position\n  )\n\n  t.assert.strictEqual(result, 'text/plain')\n  t.assert.strictEqual(position.position, input.indexOf(';'))\n})\n"
  },
  {
    "path": "test/install.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst undici = require('../index')\nconst { installedExports } = require('../lib/global')\n\ntest('install() should overwrite only specified elements in globalThis', () => {\n  const exportsToCheck = new Set(installedExports)\n\n  // Use a Proxy to verify that only the expected globals are set\n  // and no other properties are added to globalThis by undici.install().\n  const proxyGlobalThis = new Proxy(globalThis, {\n    set (target, prop, value) {\n      if (exportsToCheck.has(prop)) {\n        target[prop] = value\n        exportsToCheck.delete(prop)\n        return true\n      }\n      throw new Error(`Unexpected global set: ${String(prop)}`)\n    }\n  })\n\n  // eslint-disable-next-line no-global-assign\n  globalThis = proxyGlobalThis\n\n  undici.install()\n\n  assert.strictEqual(exportsToCheck.size, 0, `Some expected globals were not set: ${[...exportsToCheck].join(', ')}`)\n\n  // Verify that the installed globals match the exports from undici\n  for (const name of installedExports) {\n    assert.strictEqual(globalThis[name], undici[name])\n  }\n\n  // Test that the installed classes are functional\n  const headers = new globalThis.Headers([['content-type', 'application/json']])\n  assert.strictEqual(headers.get('content-type'), 'application/json')\n\n  const request = new globalThis.Request('https://example.com')\n  assert.strictEqual(request.url, 'https://example.com/')\n\n  const response = new globalThis.Response('test body')\n  assert.strictEqual(response.status, 200)\n\n  const formData = new globalThis.FormData()\n  formData.append('key', 'value')\n  assert.strictEqual(formData.get('key'), 'value')\n})\n"
  },
  {
    "path": "test/interceptors/cache-async-store.js",
    "content": "'use strict'\n\nconst { test, after, describe } = require('node:test')\nconst { strictEqual, notStrictEqual } = require('node:assert')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { Readable } = require('node:stream')\nconst { request, Client, interceptors } = require('../../index')\nconst MemoryCacheStore = require('../../lib/cache/memory-cache-store')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { setTimeout } = require('node:timers/promises')\n\n/**\n * Wraps a MemoryCacheStore to simulate an async remote store:\n * - get() always returns a Promise\n * - body is returned as a Readable stream instead of an array\n */\nclass AsyncCacheStore {\n  #inner\n\n  constructor () {\n    this.#inner = new MemoryCacheStore()\n  }\n\n  async get (key) {\n    const result = this.#inner.get(key)\n    if (!result) return undefined\n\n    const { body, ...rest } = result\n    const readable = new Readable({ read () {} })\n    if (body) {\n      for (const chunk of body) {\n        readable.push(chunk)\n      }\n    }\n    readable.push(null)\n\n    return { ...rest, body: readable }\n  }\n\n  createWriteStream (key, value) {\n    return this.#inner.createWriteStream(key, value)\n  }\n\n  delete (key) {\n    return this.#inner.delete(key)\n  }\n}\n\ndescribe('cache interceptor with async store', () => {\n  test('stale-while-revalidate 304 refreshes cache with async store', async () => {\n    const clock = FakeTimers.install({ now: 1 })\n    after(() => clock.uninstall())\n\n    let count200 = 0\n    let count304 = 0\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.sendDate = false\n      res.setHeader('Date', new Date(clock.now).toUTCString())\n      res.setHeader('Cache-Control', 'public, max-age=10, stale-while-revalidate=3600')\n      res.setHeader('ETag', '\"test-etag\"')\n\n      if (req.headers['if-none-match']) {\n        count304++\n        res.statusCode = 304\n        res.end()\n      } else {\n        res.end('hello world ' + count200++)\n      }\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const store = new AsyncCacheStore()\n    const dispatcher = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({ store }))\n\n    after(async () => {\n      server.close()\n      await dispatcher.close()\n    })\n\n    const url = `http://localhost:${server.address().port}`\n\n    // First request, populates cache\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(res.statusCode, 200)\n      strictEqual(res.headers.warning, undefined)\n    }\n\n    // Advance past max-age into stale-while-revalidate window\n    clock.tick(12000)\n\n    // Second request: stale, triggers background 304 revalidation\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(res.statusCode, 200)\n      strictEqual(res.headers.warning, '110 - \"response is stale\"')\n      await setTimeout(100)\n    }\n\n    // Third request: should be fresh after 304 revalidation\n    {\n      clock.tick(10)\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(res.statusCode, 200)\n      strictEqual(res.headers.warning, undefined)\n    }\n\n    strictEqual(count200, 1)\n    strictEqual(count304, 1)\n  })\n\n  test('stale-while-revalidate 200 refreshes cache with async store', async () => {\n    const clock = FakeTimers.install({ now: 1 })\n    after(() => clock.uninstall())\n\n    let requestCount = 0\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.sendDate = false\n      res.setHeader('Date', new Date(clock.now).toUTCString())\n      res.setHeader('Cache-Control', 'public, max-age=10, stale-while-revalidate=3600')\n      res.setHeader('ETag', `\"etag-${requestCount}\"`)\n      res.end('hello world ' + requestCount++)\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const store = new AsyncCacheStore()\n    const dispatcher = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({ store }))\n\n    after(async () => {\n      server.close()\n      await dispatcher.close()\n    })\n\n    const url = `http://localhost:${server.address().port}`\n\n    // First request\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n    }\n\n    // Advance past max-age\n    clock.tick(12000)\n\n    // Stale response, triggers background 200 revalidation\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(res.headers.warning, '110 - \"response is stale\"')\n      await setTimeout(100)\n    }\n\n    // Should be fresh with new content\n    {\n      clock.tick(10)\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 1')\n      strictEqual(res.headers.warning, undefined)\n    }\n  })\n\n  test('null vary values are not sent in revalidation headers', async () => {\n    const clock = FakeTimers.install({ now: 1 })\n    after(() => clock.uninstall())\n\n    let revalidationHeaders = null\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.sendDate = false\n      res.setHeader('Date', new Date(clock.now).toUTCString())\n      res.setHeader('Cache-Control', 'public, max-age=10, stale-while-revalidate=3600')\n      res.setHeader('ETag', '\"test-etag\"')\n      res.setHeader('Vary', 'X-Custom-Header, X-Another-Header')\n\n      if (req.headers['if-none-match']) {\n        revalidationHeaders = { ...req.headers }\n        res.statusCode = 304\n        res.end()\n      } else {\n        res.end('hello world')\n      }\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const store = new AsyncCacheStore()\n    const dispatcher = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({ store }))\n\n    after(async () => {\n      server.close()\n      await dispatcher.close()\n    })\n\n    const url = `http://localhost:${server.address().port}`\n\n    // First request without X-Custom-Header or X-Another-Header\n    // These will be stored as null in the vary record\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world')\n    }\n\n    // Advance past max-age\n    clock.tick(12000)\n\n    // Trigger stale-while-revalidate\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(res.headers.warning, '110 - \"response is stale\"')\n      await setTimeout(100)\n    }\n\n    // Verify the revalidation request did NOT include null vary headers\n    notStrictEqual(revalidationHeaders, null)\n    strictEqual(revalidationHeaders['x-custom-header'], undefined)\n    strictEqual(revalidationHeaders['x-another-header'], undefined)\n    strictEqual(revalidationHeaders['if-none-match'], '\"test-etag\"')\n  })\n})\n"
  },
  {
    "path": "test/interceptors/cache-query-params.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { equal, notEqual } = require('node:assert')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { Client, request, interceptors } = require('../../')\n\ntest('query parameters create separate cache entries', async () => {\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n    const url = new URL(req.url, 'http://localhost')\n    const param = url.searchParams.get('param') || 'default'\n\n    res.writeHead(200, {\n      'Content-Type': 'application/json',\n      'Cache-Control': 'public, max-age=100'\n    })\n    res.end(JSON.stringify({\n      message: `Response for param=${param}`,\n      requestNumber: requestCount\n    }))\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n    .compose(interceptors.cache())\n\n  after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  const origin = `http://localhost:${server.address().port}`\n\n  // First request with param=value1\n  const response1 = await request(origin, {\n    dispatcher: client,\n    query: { param: 'value1' }\n  })\n  const body1 = await response1.body.text()\n  equal(requestCount, 1, 'First request should hit the server')\n\n  // Second request with same param - should be cached\n  const response2 = await request(origin, {\n    dispatcher: client,\n    query: { param: 'value1' }\n  })\n  const body2 = await response2.body.text()\n  equal(requestCount, 1, 'Second request with same query should be cached')\n  equal(body1, body2, 'Cached response should match original')\n\n  // Third request with different param - should NOT be cached\n  const response3 = await request(origin, {\n    dispatcher: client,\n    query: { param: 'value2' }\n  })\n  const body3 = await response3.body.text()\n  equal(requestCount, 2, 'Request with different query should hit the server')\n  notEqual(body1, body3, 'Different query parameters should create separate cache entries')\n})\n\ntest('complex query parameters are handled correctly', async () => {\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n\n    res.writeHead(200, {\n      'Content-Type': 'application/json',\n      'Cache-Control': 'public, max-age=100'\n    })\n    res.end(JSON.stringify({\n      url: req.url,\n      requestNumber: requestCount\n    }))\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n    .compose(interceptors.cache())\n\n  after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  const origin = `http://localhost:${server.address().port}`\n\n  // Complex query with arrays and multiple parameters\n  const complexQuery = {\n    search: 'hello world',\n    tags: ['javascript', 'nodejs'],\n    limit: 10,\n    active: true\n  }\n\n  // First request\n  const response1 = await request(origin, {\n    dispatcher: client,\n    query: complexQuery\n  })\n  const body1 = await response1.body.text()\n  equal(requestCount, 1, 'First complex query should hit the server')\n\n  // Same complex query - should be cached\n  const response2 = await request(origin, {\n    dispatcher: client,\n    query: complexQuery\n  })\n  const body2 = await response2.body.text()\n  equal(requestCount, 1, 'Same complex query should be cached')\n  equal(body1, body2, 'Complex query parameters should be cached correctly')\n\n  // Slightly different query - should NOT be cached\n  const response3 = await request(origin, {\n    dispatcher: client,\n    query: {\n      search: 'hello world',\n      tags: ['javascript', 'nodejs'],\n      limit: 20, // Different limit\n      active: true\n    }\n  })\n  const body3 = await response3.body.text()\n  equal(requestCount, 2, 'Different complex query should hit the server')\n  notEqual(body1, body3, 'Different query parameters should create separate cache entries')\n})\n\ntest('query parameters vs path equivalence', async () => {\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n\n    res.writeHead(200, {\n      'Content-Type': 'application/json',\n      'Cache-Control': 'public, max-age=100'\n    })\n    res.end(JSON.stringify({\n      url: req.url,\n      requestNumber: requestCount\n    }))\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n    .compose(interceptors.cache())\n\n  after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  const origin = `http://localhost:${server.address().port}`\n\n  // Request using query object\n  const response1 = await request(origin, {\n    dispatcher: client,\n    query: { foo: 'bar', baz: 'qux' }\n  })\n  const body1 = await response1.body.text()\n  equal(requestCount, 1, 'Query object request should hit the server')\n\n  // Request using path with query string - should be cached if URLs match\n  const response2 = await request(`${origin}/?foo=bar&baz=qux`, {\n    dispatcher: client\n  })\n  const body2 = await response2.body.text()\n  equal(requestCount, 1, 'Equivalent path query should be cached')\n  equal(body1, body2, 'Query object and path query should be equivalent')\n})\n\ntest('empty and undefined query parameters', async () => {\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n\n    res.writeHead(200, {\n      'Content-Type': 'application/json',\n      'Cache-Control': 'public, max-age=100'\n    })\n    res.end(JSON.stringify({\n      url: req.url,\n      requestNumber: requestCount\n    }))\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n    .compose(interceptors.cache())\n\n  after(async () => {\n    server.close()\n    await client.close()\n  })\n\n  const origin = `http://localhost:${server.address().port}`\n\n  // Request with no query\n  const response1 = await request(origin, { dispatcher: client })\n  const body1 = await response1.body.text()\n  equal(requestCount, 1, 'No query request should hit the server')\n\n  // Request with empty query object - should be cached\n  const response2 = await request(origin, {\n    dispatcher: client,\n    query: {}\n  })\n  const body2 = await response2.body.text()\n  equal(requestCount, 1, 'Empty query object should be cached')\n  equal(body1, body2, 'No query and empty query should be equivalent')\n\n  // Request with undefined query - should be cached\n  const response3 = await request(origin, {\n    dispatcher: client,\n    query: undefined\n  })\n  const body3 = await response3.body.text()\n  equal(requestCount, 1, 'Undefined query should be cached')\n  equal(body1, body3, 'No query and undefined query should be equivalent')\n})\n"
  },
  {
    "path": "test/interceptors/cache-revalidate-stale.js",
    "content": "'use strict'\n\nconst { test, after, describe } = require('node:test')\nconst { strictEqual } = require('node:assert')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { request, Client, interceptors } = require('../../index')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { setTimeout } = require('node:timers/promises')\n\ntest('revalidates the request when the response is stale', async () => {\n  const clock = FakeTimers.install({\n    now: 1\n  })\n  after(() => clock.uninstall())\n\n  let count = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.sendDate = false\n    res.setHeader('Date', new Date(clock.now).toUTCString())\n    res.setHeader('Cache-Control', 'public, max-age=1')\n    res.end('hello world ' + count++)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const dispatcher = new Client(`http://localhost:${server.address().port}`)\n    .compose(interceptors.cache())\n\n  after(async () => {\n    server.close()\n    await dispatcher.close()\n  })\n\n  const url = `http://localhost:${server.address().port}`\n\n  {\n    const res = await request(url, { dispatcher })\n    strictEqual(await res.body.text(), 'hello world 0')\n  }\n\n  clock.tick(999)\n\n  {\n    const res = await request(url, { dispatcher })\n    strictEqual(await res.body.text(), 'hello world 0')\n  }\n\n  clock.tick(1)\n\n  {\n    const res = await request(url, { dispatcher })\n    strictEqual(await res.body.text(), 'hello world 1')\n  }\n\n  clock.tick(999)\n\n  {\n    const res = await request(url, { dispatcher })\n    strictEqual(await res.body.text(), 'hello world 1')\n  }\n\n  clock.tick(1)\n\n  {\n    const res = await request(url, { dispatcher })\n    strictEqual(await res.body.text(), 'hello world 2')\n  }\n})\n\ndescribe('revalidates the request, handles 304s during stale-while-revalidate', async () => {\n  function isStale (res) {\n    return res.headers.warning === '110 - \"response is stale\"'\n  }\n\n  async function revalidateTest (useEtag = false) {\n    const clock = FakeTimers.install({\n      now: 1\n    })\n    after(() => clock.uninstall())\n\n    let count200 = 0\n    let count304 = 0\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.sendDate = false\n      res.setHeader('Date', new Date(clock.now).toUTCString())\n      res.setHeader('Cache-Control', 'public, max-age=10, stale-while-revalidate=3600')\n      if (useEtag) {\n        res.setHeader('ETag', '\"xxx\"')\n      }\n\n      // revalidation response.\n      if (req.headers['if-none-match'] || req.headers['if-modified-since']) {\n        count304++\n        res.statusCode = 304\n        res.end()\n      } else {\n        res.end('hello world ' + count200++)\n      }\n    })\n\n    server.listen(0)\n    await once(server, 'listening')\n\n    const dispatcher = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await dispatcher.close()\n    })\n\n    const url = `http://localhost:${server.address().port}`\n\n    // initial request, cache the response\n    {\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(isStale(res), false)\n      strictEqual(res.statusCode, 200)\n    }\n\n    // wait nearly a second, still fresh\n    {\n      clock.tick(900)\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(isStale(res), false)\n      strictEqual(res.statusCode, 200)\n    }\n\n    // within stale-while-revalidate window, still return stale cached response, revalidate in background\n    {\n      clock.tick(12000)\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(isStale(res), true)\n      strictEqual(res.statusCode, 200)\n      await setTimeout(100) // wait for revalidation to be complete.\n    }\n\n    // should get revalidated content, not stale.\n    {\n      clock.tick(10)\n      const res = await request(url, { dispatcher })\n      strictEqual(await res.body.text(), 'hello world 0')\n      strictEqual(isStale(res), false)\n      strictEqual(res.statusCode, 200)\n    }\n\n    strictEqual(count200, 1)\n    strictEqual(count304, 1)\n  }\n\n  test('using if-none-match', async () => await revalidateTest(true))\n  test('using if-modified-since', async () => await revalidateTest(false))\n})\n"
  },
  {
    "path": "test/interceptors/cache.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { describe, test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { equal, strictEqual, notEqual, fail } = require('node:assert')\nconst { setTimeout: sleep } = require('node:timers/promises')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { Client, interceptors, cacheStores: { MemoryCacheStore } } = require('../../index')\nconst { makeCacheKey } = require('../../lib/util/cache.js')\n\ndescribe('Cache Interceptor', () => {\n  test('caches request', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestsToOrigin++\n      res.setHeader('cache-control', 's-maxage=10')\n      res.end('asd')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Sanity check\n    equal(requestsToOrigin, 0)\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n  })\n\n  test('vary directives used to decide which response to use', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      requestsToOrigin++\n      res.setHeader('cache-control', 's-maxage=10')\n      res.setHeader('vary', 'a')\n\n      if (req.headers.a === 'asd123') {\n        res.end('asd')\n      } else {\n        res.end('dsa')\n      }\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const requestA = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        a: 'asd123'\n      }\n    }\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const requestB = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        a: 'dsa'\n      }\n    }\n\n    // Should reach origin\n    {\n      const res = await client.request(requestA)\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // Should reach origin\n    {\n      const res = await client.request(requestB)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'dsa')\n    }\n\n    // Should be cached\n    {\n      const res = await client.request(requestA)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // Should be cached\n    {\n      const res = await client.request(requestB)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'dsa')\n    }\n  })\n\n  test('revalidates reponses with no-cache directive, regardless of cacheByDefault', async () => {\n    let requestCount = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      ++requestCount\n      res.setHeader('Vary', 'Accept-Encoding')\n      res.setHeader('cache-control', 'no-cache')\n      res.end(`Request count: ${requestCount}`)\n    }).listen(0)\n\n    after(async () => {\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    await once(server, 'listening')\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({\n        cacheByDefault: 1000\n      }))\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    const res1 = await client.request(request)\n    const body1 = await res1.body.text()\n    strictEqual(body1, 'Request count: 1')\n    strictEqual(requestCount, 1)\n\n    const res2 = await client.request(request)\n    const body2 = await res2.body.text()\n    strictEqual(body2, 'Request count: 2')\n    strictEqual(requestCount, 2)\n  })\n\n  test('expires caching', async () => {\n    const clock = FakeTimers.install({\n      shouldClearNativeTimers: true\n    })\n\n    let requestsToOrigin = 0\n    let serverError\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const now = new Date()\n      now.setSeconds(now.getSeconds() + 1)\n      res.setHeader('date', 0)\n      res.setHeader('expires', now.toGMTString())\n      requestsToOrigin++\n      res.end('asd')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n      clock.uninstall()\n    })\n\n    await once(server, 'listening')\n\n    strictEqual(requestsToOrigin, 0)\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send initial request. This should reach the origin\n    {\n      const res = await client.request(request)\n      if (serverError) {\n        throw serverError\n      }\n\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // This is cached\n    {\n      const res = await client.request(request)\n      if (serverError) {\n        throw serverError\n      }\n\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    clock.tick(1500)\n\n    // Response is now stale, the origin should get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // Response is now cached, the origin should not get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n  })\n\n  test('expires caching with Etag', async () => {\n    const clock = FakeTimers.install({\n      shouldClearNativeTimers: true\n    })\n\n    let requestsToOrigin = 0\n    let serverError\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const now = new Date()\n      now.setSeconds(now.getSeconds() + 1)\n      res.setHeader('date', 0)\n      res.setHeader('expires', now.toGMTString())\n      res.setHeader('etag', 'asd123')\n      requestsToOrigin++\n      res.end('asd')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n      clock.uninstall()\n    })\n\n    await once(server, 'listening')\n\n    strictEqual(requestsToOrigin, 0)\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send initial request. This should reach the origin\n    {\n      const res = await client.request(request)\n      if (serverError) {\n        throw serverError\n      }\n\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // This is cached\n    {\n      const res = await client.request(request)\n      if (serverError) {\n        throw serverError\n      }\n\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    clock.tick(1500)\n\n    // Response is now stale, the origin should get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // Response is now cached, the origin should not get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n  })\n\n  test('max-age caching', async () => {\n    const clock = FakeTimers.install({\n      shouldClearNativeTimers: true\n    })\n\n    let requestsToOrigin = 0\n    let serverError\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('date', 0)\n      res.setHeader('cache-control', 's-maxage=1')\n      requestsToOrigin++\n      res.end('asd')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n      clock.uninstall()\n    })\n\n    await once(server, 'listening')\n\n    strictEqual(requestsToOrigin, 0)\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send initial request. This should reach the origin\n    {\n      const res = await client.request(request)\n      if (serverError) {\n        throw serverError\n      }\n\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    clock.tick(1500)\n\n    // Response is now stale, the origin should get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n\n    // Response is now cached, the origin should not get a request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      strictEqual(await res.body.text(), 'asd')\n    }\n  })\n\n  test('vary headers are present in revalidation request', async () => {\n    const clock = FakeTimers.install({\n      shouldClearNativeTimers: true\n    })\n\n    let requestsToOrigin = 0\n    let revalidationRequests = 0\n    let serverError\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('date', 0)\n      res.setHeader('cache-control', 's-maxage=1, stale-while-revalidate=10')\n\n      try {\n        const ifNoneMatch = req.headers['if-none-match']\n\n        if (ifNoneMatch) {\n          revalidationRequests++\n          notEqual(req.headers.a, undefined)\n          notEqual(req.headers['b-mixed-case'], undefined)\n\n          res.statusCode = 304\n          res.end()\n        } else {\n          requestsToOrigin++\n          res.setHeader('vary', 'a, B-MIXED-CASe')\n          res.setHeader('etag', '\"asd\"')\n          res.end('asd')\n        }\n      } catch (err) {\n        serverError = err\n        res.end()\n      }\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n      clock.uninstall()\n    })\n\n    await once(server, 'listening')\n\n    strictEqual(requestsToOrigin, 0)\n    strictEqual(revalidationRequests, 0)\n\n    const request = {\n      origin: 'localhost',\n      path: '/',\n      method: 'GET'\n    }\n\n    {\n      const response = await client.request({\n        ...request,\n        headers: {\n          a: 'asd',\n          'b-Mixed-case': 'asd'\n        }\n      })\n      if (serverError) {\n        throw serverError\n      }\n\n      strictEqual(requestsToOrigin, 1)\n      strictEqual(await response.body.text(), 'asd')\n    }\n\n    clock.tick(1500)\n\n    {\n      const response = await client.request({\n        ...request,\n        headers: {\n          a: 'asd',\n          'B-mixed-CASE': 'asd'\n        }\n      })\n      if (serverError) {\n        throw serverError\n      }\n\n      strictEqual(requestsToOrigin, 1)\n      strictEqual(await response.body.text(), 'asd')\n    }\n\n    // Wait for background revalidation to complete\n    await sleep(100)\n    strictEqual(revalidationRequests, 1)\n  })\n\n  test('unsafe methods cause resource to be purged from cache', async () => {\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => res.end('asd')).listen(0)\n\n    after(() => server.close())\n    await once(server, 'listening')\n\n    const store = new MemoryCacheStore()\n\n    let deleteCalled = false\n    const originalDelete = store.delete.bind(store)\n    store.delete = (key) => {\n      deleteCalled = true\n      originalDelete(key)\n    }\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({\n        store,\n        methods: ['GET'] // explicitly only cache GET methods\n      }))\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send initial request, will cache the response\n    await client.request(request)\n\n    // Sanity check\n    equal(deleteCalled, false)\n\n    // Make sure the common unsafe methods cause cache purges\n    for (const method of ['POST', 'PUT', 'PATCH', 'DELETE']) {\n      deleteCalled = false\n\n      await client.request({\n        ...request,\n        method\n      })\n\n      equal(deleteCalled, true, method)\n    }\n  })\n\n  test('unsafe methods aren\\'t cached', async () => {\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      res.setHeader('cache-control', 'public, s-maxage=1')\n      res.end('')\n    }).listen(0)\n\n    after(() => server.close())\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({\n        store: {\n          get () {\n            return undefined\n          },\n          createWriteStream (key) {\n            fail(key.method)\n          },\n          delete () { }\n        }\n      }))\n\n    for (const method of ['POST', 'PUT', 'PATCH', 'DELETE']) {\n      await client.request({\n        origin: 'localhost',\n        method,\n        path: '/'\n      })\n    }\n  })\n\n  test('necessary headers are stripped', async () => {\n    const headers = [\n      // Headers defined in the spec that we need to strip\n      'connection',\n      'proxy-authenticate',\n      'proxy-authentication-info',\n      'proxy-authorization',\n      'proxy-connection',\n      'te',\n      'upgrade',\n      // Headers we need to specifiy to be stripped\n      'should-be-stripped'\n    ]\n\n    let requestToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestToOrigin++\n      res.setHeader('cache-control', 's-maxage=10, no-cache=should-be-stripped')\n      res.setHeader('should-not-be-stripped', 'asd')\n\n      for (const header of headers) {\n        res.setHeader(header, 'asd')\n      }\n\n      res.end()\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    {\n      const res = await client.request(request)\n      equal(requestToOrigin, 1)\n      equal(res.headers['should-not-be-stripped'], 'asd')\n\n      for (const header of headers) {\n        equal(res.headers[header], 'asd')\n      }\n    }\n\n    {\n      const res = await client.request(request)\n      equal(requestToOrigin, 1)\n      equal(res.headers['should-not-be-stripped'], 'asd')\n      equal(res.headers['transfer-encoding'], undefined)\n\n      for (const header of headers) {\n        equal(res.headers[header], undefined)\n      }\n    }\n  })\n\n  test('cacheByDefault', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestsToOrigin++\n      res.end('asd')\n    }).listen(0)\n\n    after(() => server.close())\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache({\n        cacheByDefault: 3600\n      }))\n\n    equal(requestsToOrigin, 0)\n\n    // Should hit the origin\n    {\n      const res = await client.request({\n        origin: 'localhost',\n        path: '/',\n        method: 'GET'\n      })\n      equal(requestsToOrigin, 1)\n      equal(await res.body.text(), 'asd')\n    }\n\n    // Should hit the cache\n    {\n      const res = await client.request({\n        origin: 'localhost',\n        path: '/',\n        method: 'GET'\n      })\n      equal(requestsToOrigin, 1)\n      equal(await res.body.text(), 'asd')\n    }\n  })\n\n  test('stale-if-error (response)', async () => {\n    const clock = FakeTimers.install({\n      shouldClearNativeTimers: true\n    })\n\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      res.setHeader('date', 0)\n\n      requestsToOrigin++\n      if (requestsToOrigin === 1) {\n        // First request\n        res.setHeader('cache-control', 'public, s-maxage=10, stale-if-error=20')\n        res.end('asd')\n      } else {\n        res.statusCode = 500\n        res.end('')\n      }\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      clock.uninstall()\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    strictEqual(requestsToOrigin, 0)\n\n    /**\n     * @type {import('../../types/dispatcher').default.RequestOptions}\n     */\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send first request. This will hit the origin and succeed\n    {\n      const response = await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(response.statusCode, 200)\n      equal(await response.body.text(), 'asd')\n    }\n\n    // Send second request. It isn't stale yet, so this should be from the\n    //  cache and succeed\n    {\n      const response = await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(response.statusCode, 200)\n      equal(await response.body.text(), 'asd')\n    }\n\n    clock.tick(15000)\n\n    // Send third request. This is now stale, the revalidation request should\n    //  fail but the response should still be served from cache.\n    {\n      const response = await client.request(request)\n      equal(requestsToOrigin, 2)\n      equal(response.statusCode, 200)\n      equal(await response.body.text(), 'asd')\n    }\n\n    clock.tick(25000)\n\n    // Send fourth request. We're now outside the stale-if-error threshold and\n    //  should see the error.\n    {\n      const response = await client.request(request)\n      equal(requestsToOrigin, 3)\n      equal(response.statusCode, 500)\n    }\n  })\n\n  describe('Client-side directives', () => {\n    test('max-age', async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('date', 0)\n        res.setHeader('cache-control', 'public, s-maxage=100')\n        res.end()\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        clock.uninstall()\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      strictEqual(requestsToOrigin, 0)\n\n      /**\n       * @type {import('../../types/dispatcher').default.RequestOptions}\n       */\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // Send first request to cache the response\n      await client.request(request)\n      equal(requestsToOrigin, 1)\n\n      // Send second request, should be served by the cache since it's within\n      //  the window\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'max-age=5'\n        }\n      })\n      equal(requestsToOrigin, 1)\n\n      clock.tick(6000)\n\n      // Send third request, should reach the origin\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'max-age=5'\n        }\n      })\n      equal(requestsToOrigin, 2)\n    })\n\n    test('max-stale', async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      let requestsToOrigin = 0\n      let revalidationRequests = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        res.setHeader('date', 0)\n        res.setHeader('cache-control', 'public, s-maxage=1, stale-while-revalidate=10')\n\n        if (req.headers['if-modified-since']) {\n          revalidationRequests++\n          res.statusCode = 304\n          res.end()\n        } else {\n          requestsToOrigin++\n          res.end('asd')\n        }\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        server.close()\n        await client.close()\n        clock.uninstall()\n      })\n\n      await once(server, 'listening')\n\n      strictEqual(requestsToOrigin, 0)\n\n      /**\n       * @type {import('../../types/dispatcher').default.RequestOptions}\n       */\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(revalidationRequests, 0)\n\n      clock.tick(1500)\n\n      // Send second request within the max-stale threshold\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'max-stale=5'\n        }\n      })\n      equal(requestsToOrigin, 1)\n      equal(revalidationRequests, 0)\n\n      // Send third request outside the max-stale threshold\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'max-stale=0'\n        }\n      })\n      equal(requestsToOrigin, 1)\n\n      // Wait for background revalidation to complete\n      await sleep(100)\n      equal(revalidationRequests, 1)\n    })\n\n    test('min-fresh', async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('date', 0)\n        res.setHeader('cache-control', 'public, s-maxage=10')\n        res.end()\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        server.close()\n        await client.close()\n        clock.uninstall()\n      })\n\n      await once(server, 'listening')\n\n      strictEqual(requestsToOrigin, 0)\n\n      /**\n       * @type {import('../../types/dispatcher').default.RequestOptions}\n       */\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      await client.request(request)\n      equal(requestsToOrigin, 1)\n\n      // Fast forward to response having 8sec ttl\n      clock.tick(2000)\n\n      // Send request within the threshold\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'min-fresh=5'\n        }\n      })\n      equal(requestsToOrigin, 1)\n\n      // Fast forward again, response has 2sec ttl\n      clock.tick(6000)\n\n      await client.request({\n        ...request,\n        headers: {\n          'cache-control': 'min-fresh=5'\n        }\n      })\n      equal(requestsToOrigin, 2)\n    })\n\n    test('no-cache', async () => {\n      let requestsToOrigin = 0\n      let revalidationRequests = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        if (req.headers['if-modified-since']) {\n          revalidationRequests++\n          res.statusCode = 304\n          res.end()\n        } else {\n          requestsToOrigin++\n          res.setHeader('cache-control', 'public, s-maxage=100')\n          res.end('asd')\n        }\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      strictEqual(requestsToOrigin, 0)\n\n      // Send initial request. This should reach the origin\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: {\n          'cache-control': 'no-cache'\n        }\n      })\n      strictEqual(requestsToOrigin, 1)\n      strictEqual(revalidationRequests, 0)\n\n      // Send second request, a validation request should be sent\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: {\n          'cache-control': 'no-cache'\n        }\n      })\n      strictEqual(requestsToOrigin, 1)\n      strictEqual(revalidationRequests, 1)\n\n      // Send third request w/o no-cache, this should be handled by the cache\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      })\n      strictEqual(requestsToOrigin, 1)\n      strictEqual(revalidationRequests, 1)\n    })\n\n    test('no-store', async () => {\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('cache-control', 'public, s-maxage=100')\n        res.end('asd')\n      }).listen(0)\n\n      const store = new MemoryCacheStore()\n      store.createWriteStream = () => {\n        fail('shouln\\'t have reached this')\n      }\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({ store }))\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: {\n          'cache-control': 'no-store'\n        }\n      })\n    })\n\n    test('only-if-cached', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('cache-control', 'public, s-maxage=100')\n        res.end('asd')\n        requestsToOrigin++\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      // Send initial request. This should reach the origin\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      })\n      equal(requestsToOrigin, 1)\n\n      // Send second request, this shouldn't reach the origin\n      await client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: {\n          'cache-control': 'only-if-cached'\n        }\n      })\n      equal(requestsToOrigin, 1)\n\n      // Send third request to an uncached resource, this should return a 504\n      {\n        const res = await client.request({\n          origin: 'localhost',\n          method: 'GET',\n          path: '/bla',\n          headers: {\n            'cache-control': 'only-if-cached'\n          }\n        })\n        equal(res.statusCode, 504)\n      }\n\n      // Send fourth request to an uncached resource w/ a , this should return a 504\n      {\n        const res = await client.request({\n          origin: 'localhost',\n          method: 'GET',\n          path: '/asd123',\n          headers: {\n            'cache-control': 'only-if-cached'\n          }\n        })\n        equal(res.statusCode, 504)\n      }\n    })\n\n    test('stale-if-error', async () => {\n      const clock = FakeTimers.install({\n        shouldClearNativeTimers: true\n      })\n\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('date', 0)\n\n        requestsToOrigin++\n        if (requestsToOrigin === 1) {\n          // First request, send stale-while-revalidate to keep the value in the cache\n          res.setHeader('cache-control', 'public, s-maxage=10, stale-while-revalidate=20')\n          res.end('asd')\n        } else {\n          res.statusCode = 500\n          res.end('')\n        }\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        clock.uninstall()\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      strictEqual(requestsToOrigin, 0)\n\n      // Send first request. This will hit the origin and succeed\n      {\n        const response = await client.request({\n          origin: 'localhost',\n          path: '/',\n          method: 'GET'\n        })\n        equal(requestsToOrigin, 1)\n        equal(response.statusCode, 200)\n        equal(await response.body.text(), 'asd')\n      }\n\n      // Send second request. It isn't stale yet, so this should be from the\n      //  cache and succeed\n      {\n        const response = await client.request({\n          origin: 'localhost',\n          path: '/',\n          method: 'GET'\n        })\n        equal(requestsToOrigin, 1)\n        equal(response.statusCode, 200)\n        equal(await response.body.text(), 'asd')\n      }\n\n      clock.tick(15000)\n\n      // Send third request. This is now stale but within stale-while-revalidate,\n      // should return stale immediately and trigger background revalidation\n      {\n        const response = await client.request({\n          origin: 'localhost',\n          path: '/',\n          method: 'GET',\n          headers: {\n            'cache-control': 'stale-if-error=20'\n          }\n        })\n        equal(response.statusCode, 200)\n        equal(await response.body.text(), 'asd')\n      }\n\n      // Wait for background revalidation to complete (which will fail with 500)\n      await sleep(100)\n      equal(requestsToOrigin, 2)\n\n      // Send a fourth request. Still within stale-while-revalidate but without stale-if-error,\n      // should return stale since previous revalidation failed and stale-if-error applies\n      {\n        const response = await client.request({\n          origin: 'localhost',\n          path: '/',\n          method: 'GET'\n        })\n        equal(response.statusCode, 200)\n        equal(await response.body.text(), 'asd')\n      }\n\n      // Wait for another background revalidation\n      await sleep(100)\n      equal(requestsToOrigin, 3)\n\n      clock.tick(25000)\n\n      // Send fifth request. We're now outside the stale-if-error threshold and\n      //  should see the error.\n      {\n        const response = await client.request({\n          origin: 'localhost',\n          path: '/',\n          method: 'GET',\n          headers: {\n            'cache-control': 'stale-if-error=20'\n          }\n        })\n        equal(requestsToOrigin, 4)\n        equal(response.statusCode, 500)\n      }\n    })\n  })\n\n  // Partial list.\n  const cacheableStatusCodes = [\n    { code: 204, body: '' },\n    { code: 302, body: 'Found' },\n    { code: 307, body: 'Temporary Redirect' },\n    { code: 404, body: 'Not Found' },\n    { code: 410, body: 'Gone' }\n  ]\n\n  for (const { code, body } of cacheableStatusCodes) {\n    test(`caches ${code} response with cache headers`, async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.statusCode = code\n        res.setHeader('cache-control', 'public, max-age=60')\n        res.end(body)\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      equal(requestsToOrigin, 0)\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit the origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        equal(res.statusCode, code)\n        strictEqual(await res.body.text(), body)\n      }\n\n      // Second request should be served from cache\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1) // Should still be 1 (cached)\n        equal(res.statusCode, code)\n        strictEqual(await res.body.text(), body)\n      }\n    })\n  }\n\n  // Partial list.\n  const nonHeuristicallyCacheableStatusCodes = [\n    { code: 201, body: 'Created' },\n    { code: 307, body: 'Temporary Redirect' },\n    { code: 418, body: 'I am a teapot' }\n  ]\n\n  for (const { code, body } of nonHeuristicallyCacheableStatusCodes) {\n    test(`does not cache non-heuristically cacheable status ${code} without explicit directive`, async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.statusCode = code\n        // By default the response may have a date and last-modified header set to 'now',\n        // causing the cache to compute a 0 heuristic expiry, causing the test to not ascertain\n        // it is really not cached.\n        res.setHeader('date', '')\n        res.end(body)\n      }).listen(0)\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({ cacheByDefault: 60 }))\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      equal(requestsToOrigin, 0)\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit the origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        equal(res.statusCode, code)\n        strictEqual(await res.body.text(), body)\n      }\n\n      // Second request should also hit the origin (not cached)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 2) // Should be 2 (not cached)\n        equal(res.statusCode, code)\n        strictEqual(await res.body.text(), body)\n      }\n    })\n  }\n\n  test('discriminates caching of range requests, or does not cache them', async () => {\n    let requestsToOrigin = 0\n    const body = 'Fake range request response'\n    const code = 206\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestsToOrigin++\n      res.statusCode = code\n      res.setHeader('cache-control', 'public, max-age=60')\n      res.end(body)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    equal(requestsToOrigin, 0)\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        range: 'bytes=10-'\n      }\n    }\n\n    // First request should hit the origin\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n\n    // Second request with different range should hit the origin too\n    request.headers.range = 'bytes=5-'\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n  })\n\n  test('discriminates caching of conditional requests (if-none-match), or does not cache them', async () => {\n    let requestsToOrigin = 0\n    const body = ''\n    const code = 304\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestsToOrigin++\n      res.statusCode = code\n      res.setHeader('cache-control', 'public, max-age=60')\n      res.end(body)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    equal(requestsToOrigin, 0)\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        'if-none-match': 'some-etag'\n      }\n    }\n\n    // First request should hit the origin\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n\n    // Second request with different etag should hit the origin too\n    request.headers['if-none-match'] = 'another-etag'\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n  })\n\n  test('discriminates caching of conditional requests (if-modified-since), or does not cache them', async () => {\n    let requestsToOrigin = 0\n    const body = ''\n    const code = 304\n    const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n      requestsToOrigin++\n      res.statusCode = code\n      res.setHeader('cache-control', 'public, max-age=60')\n      res.end(body)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    equal(requestsToOrigin, 0)\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        'if-modified-since': new Date().toUTCString()\n      }\n    }\n\n    // First request should hit the origin\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n\n    // Second request with different since should hit the origin too\n    request.headers['if-modified-since'] = new Date(0).toUTCString()\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 2)\n      equal(res.statusCode, code)\n      strictEqual(await res.body.text(), body)\n    }\n  })\n\n  test('stale-while-revalidate returns stale immediately and revalidates in background (RFC 5861)', async () => {\n    let requestsToOrigin = 0\n    let revalidationRequests = 0\n    let serverResponse = 'original-response'\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const responseDate = new Date()\n      res.setHeader('date', responseDate.toUTCString())\n      res.setHeader('cache-control', 's-maxage=1, stale-while-revalidate=10')\n\n      if (req.headers['if-modified-since']) {\n        revalidationRequests++\n        // Return updated content on revalidation\n        serverResponse = 'revalidated-response'\n        res.end(serverResponse)\n      } else {\n        requestsToOrigin++\n        res.end(serverResponse)\n      }\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send initial request to cache the response\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'original-response')\n    }\n\n    // Wait for response to become stale\n    await sleep(1100)\n\n    // Request stale content - should return immediately with stale content\n    const startTime = Date.now()\n    {\n      const res = await client.request(request)\n      const responseTime = Date.now() - startTime\n\n      // Should return stale content immediately (< 50ms)\n      equal(res.statusCode, 200)\n      strictEqual(await res.body.text(), 'original-response')\n      equal(requestsToOrigin, 1) // No additional origin requests yet\n\n      // Response should be immediate (RFC 5861 requirement)\n      if (responseTime > 100) {\n        fail(`stale-while-revalidate response took ${responseTime}ms, should be < 100ms`)\n      }\n    }\n\n    // Wait for background revalidation to complete\n    await sleep(500)\n\n    // Verify that revalidation occurred in background\n    equal(revalidationRequests, 1, 'Background revalidation should have occurred')\n  })\n\n  test('stale-while-revalidate updates cache after background revalidation (receiving new data)', async () => {\n    let requestsToOrigin = 0\n    let revalidationRequests = 0\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const responseDate = new Date()\n      res.setHeader('date', responseDate.toUTCString())\n      res.setHeader('cache-control', 's-maxage=1, stale-while-revalidate=10')\n\n      if (req.headers['if-modified-since']) {\n        revalidationRequests++\n        // Return updated content\n        res.end('updated-response')\n      } else {\n        requestsToOrigin++\n        res.end('original-response')\n      }\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Initial request\n    {\n      const res = await client.request(request)\n      equal(requestsToOrigin, 1)\n      strictEqual(await res.body.text(), 'original-response')\n    }\n\n    // Wait for staleness\n    await sleep(1100)\n\n    // First stale request - gets stale content immediately\n    {\n      const res = await client.request(request)\n      strictEqual(await res.body.text(), 'original-response')\n    }\n\n    // Wait for background revalidation\n    await sleep(500)\n    equal(revalidationRequests, 1)\n\n    // Second stale request - should get updated content from cache\n    // (still within stale-while-revalidate window)\n    {\n      const res = await client.request(request)\n      strictEqual(await res.body.text(), 'updated-response')\n      equal(requestsToOrigin, 1) // Still only one origin request\n    }\n  })\n\n  describe('origins option', () => {\n    test('caches request when origin matches string in whitelist', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: ['localhost']\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n\n      // Second request should be served from cache\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n    })\n\n    test('skips caching when origin does not match string in whitelist', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('not cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: ['http://example.com']\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n\n      // Second request should also hit origin (not cached)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 2)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n    })\n\n    test('caches request when origin matches RegExp in whitelist', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: [/localhost/]\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n\n      // Second request should be served from cache\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n    })\n\n    test('skips caching when origin does not match RegExp in whitelist', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('not cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: [/example\\.com/]\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n\n      // Second request should also hit origin (not cached)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 2)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n    })\n\n    test('caches request when origin matches any entry in mixed array', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: ['http://other.com', /localhost/]\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n\n      // Second request should be served from cache (matches RegExp)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n    })\n\n    test('caches all origins when origins option is undefined (default behavior)', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache())\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n\n      // Second request should be served from cache\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n    })\n\n    test('caches nothing when origins is empty array', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('not cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: []\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n\n      // Second request should also hit origin (not cached)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 2)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n    })\n\n    test('throws TypeError when origins is not an array', async () => {\n      const { throws } = require('node:assert')\n\n      throws(\n        () => interceptors.cache({ origins: 'http://example.com' }),\n        {\n          name: 'TypeError',\n          message: /expected opts\\.origins to be an array or undefined/i\n        }\n      )\n    })\n\n    test('throws TypeError when origins array contains invalid type', async () => {\n      const { throws } = require('node:assert')\n\n      throws(\n        () => interceptors.cache({ origins: [123] }),\n        {\n          name: 'TypeError',\n          message: /expected opts\\.origins\\[0\\] to be a string or RegExp/i\n        }\n      )\n    })\n\n    test('string matching is case insensitive', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: ['LOCALHOST']\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n\n      // Second request should be served from cache (case insensitive match)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'cached')\n      }\n    })\n\n    test('different hosts are treated as different origins', async () => {\n      let requestsToOrigin = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        requestsToOrigin++\n        res.setHeader('cache-control', 's-maxage=10')\n        res.end('not cached')\n      }).listen(0)\n\n      after(() => server.close())\n      await once(server, 'listening')\n\n      const client = new Client(`http://localhost:${server.address().port}`)\n        .compose(interceptors.cache({\n          origins: ['example.com']\n        }))\n\n      after(() => client.close())\n\n      const request = {\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }\n\n      // First request should hit origin\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 1)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n\n      // Second request should also hit origin (different host = different origin)\n      {\n        const res = await client.request(request)\n        equal(requestsToOrigin, 2)\n        strictEqual(await res.body.text(), 'not cached')\n      }\n    })\n  })\n\n  describe('determineDeleteAt', () => {\n    test('max-age response has deleteAt proportional to freshness lifetime, not 1 year', async () => {\n      const clock = FakeTimers.install({ now: 1000 })\n      after(() => clock.uninstall())\n\n      const store = new MemoryCacheStore()\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('cache-control', 'public, max-age=60')\n        res.setHeader('date', new Date(clock.now).toUTCString())\n        res.sendDate = false\n        res.end('short-lived')\n      }).listen(0)\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      const origin = `http://localhost:${server.address().port}`\n      const client = new Client(origin)\n        .compose(interceptors.cache({ store }))\n\n      const res = await client.request({ origin, method: 'GET', path: '/delete-at-maxage' })\n      strictEqual(await res.body.text(), 'short-lived')\n\n      const cached = store.get(makeCacheKey({ origin, method: 'GET', path: '/delete-at-maxage', headers: {} }))\n\n      notEqual(cached, undefined)\n      // deleteAt should be approximately 2x max-age (staleAt + freshnessLifetime),\n      // not 1 year out\n      const maxExpected = clock.now + (60 * 1000 * 3) // generous upper bound\n      equal(cached.deleteAt < maxExpected, true, `deleteAt (${cached.deleteAt}) should be well under 3x max-age (${maxExpected})`)\n      equal(cached.deleteAt > cached.staleAt, true, 'deleteAt should be greater than staleAt to allow revalidation')\n    })\n\n    test('immutable response has deleteAt of ~1 year', async () => {\n      const clock = FakeTimers.install({ now: 1000 })\n      after(() => clock.uninstall())\n\n      const store = new MemoryCacheStore()\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('cache-control', 'public, immutable')\n        res.setHeader('date', new Date(clock.now).toUTCString())\n        res.sendDate = false\n        res.end('immutable-content')\n      }).listen(0)\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      const origin = `http://localhost:${server.address().port}`\n      const client = new Client(origin)\n        .compose(interceptors.cache({ store }))\n\n      const res = await client.request({ origin, method: 'GET', path: '/delete-at-immutable' })\n      strictEqual(await res.body.text(), 'immutable-content')\n\n      const cached = store.get(makeCacheKey({ origin, method: 'GET', path: '/delete-at-immutable', headers: {} }))\n\n      notEqual(cached, undefined)\n      const oneYear = 31536000000\n      // deleteAt should be approximately 1 year out\n      equal(cached.deleteAt >= clock.now + oneYear - 1000, true, `deleteAt (${cached.deleteAt}) should be ~1 year out`)\n    })\n\n    test('stale-while-revalidate extends deleteAt beyond staleAt', async () => {\n      const clock = FakeTimers.install({ now: 1000 })\n      after(() => clock.uninstall())\n\n      const store = new MemoryCacheStore()\n      const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {\n        res.setHeader('cache-control', 'public, max-age=60, stale-while-revalidate=300')\n        res.setHeader('date', new Date(clock.now).toUTCString())\n        res.sendDate = false\n        res.end('swr-content')\n      }).listen(0)\n\n      after(async () => {\n        server.close()\n        await client.close()\n      })\n\n      await once(server, 'listening')\n\n      const origin = `http://localhost:${server.address().port}`\n      const client = new Client(origin)\n        .compose(interceptors.cache({ store }))\n\n      const res = await client.request({ origin, method: 'GET', path: '/delete-at-swr' })\n      strictEqual(await res.body.text(), 'swr-content')\n\n      const cached = store.get(makeCacheKey({ origin, method: 'GET', path: '/delete-at-swr', headers: {} }))\n\n      notEqual(cached, undefined)\n      // deleteAt should be staleAt + stale-while-revalidate (300s)\n      const expectedDeleteAt = cached.staleAt + (300 * 1000)\n      equal(cached.deleteAt, expectedDeleteAt, `deleteAt (${cached.deleteAt}) should be staleAt + 300s (${expectedDeleteAt})`)\n    })\n  })\n})\n"
  },
  {
    "path": "test/interceptors/decompress.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { createGzip, createDeflate, createBrotliCompress, createZstdCompress } = require('node:zlib')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst { Client, getGlobalDispatcher, setGlobalDispatcher, request } = require('../..')\nconst createDecompressInterceptor = require('../../lib/interceptor/decompress')\n\ntest('should decompress gzip response', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const data = 'This is a test message for gzip compression validation.'\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, data)\n\n  await t.completed\n})\n\ntest('should decompress deflate response', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const deflate = createDeflate()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'deflate'\n    })\n\n    const data = 'This message is compressed with deflate algorithm!'\n    deflate.pipe(res)\n    deflate.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, 'This message is compressed with deflate algorithm!')\n\n  await t.completed\n})\n\ntest('should decompress brotli response', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const brotli = createBrotliCompress()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'br'\n    })\n\n    const data = 'This message is compressed with brotli compression!'\n    brotli.pipe(res)\n    brotli.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, 'This message is compressed with brotli compression!')\n\n  await t.completed\n})\n\ntest('should decompress zstd response', { skip: typeof createZstdCompress !== 'function' }, async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const zstd = createZstdCompress()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'zstd'\n    })\n\n    const data = 'This message is compressed with zstd compression!'\n    zstd.pipe(res)\n    zstd.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, 'This message is compressed with zstd compression!')\n\n  await t.completed\n})\n\ntest('should pass through uncompressed response', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/plain'\n    })\n    res.end('This is uncompressed data')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-type'], 'text/plain')\n  t.equal(body, 'This is uncompressed data')\n\n  await t.completed\n})\n\ntest('should pass through unsupported encoding', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'unsupported'\n    })\n    res.end('This has unsupported encoding')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], 'unsupported')\n  t.equal(body, 'This has unsupported encoding')\n\n  await t.completed\n})\n\ntest('should pass through error responses (4xx, 5xx)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(404, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    const data = 'Not found error message'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 404)\n  t.equal(response.headers['content-encoding'], 'gzip')\n  t.notEqual(body, 'Not found error message')\n\n  await t.completed\n})\n\ntest('should pass through 204 No Content responses', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(204, {\n      'Content-Encoding': 'gzip'\n    })\n    res.end()\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  t.equal(response.statusCode, 204)\n  t.equal(response.headers['content-encoding'], 'gzip')\n\n  await t.completed\n})\n\ntest('should pass through 304 Not Modified responses', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(304, {\n      'Content-Encoding': 'gzip'\n    })\n    res.end()\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  t.equal(response.statusCode, 304)\n  t.equal(response.headers['content-encoding'], 'gzip')\n\n  await t.completed\n})\n\ntest('should handle large compressed responses', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    const largeData = 'A'.repeat(10000) + 'B'.repeat(10000) + 'C'.repeat(10000)\n    gzip.pipe(res)\n    gzip.end(largeData)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body.length, 30000)\n\n  await t.completed\n})\n\ntest('should handle case-insensitive content-encoding', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'GZIP' // Uppercase\n    })\n\n    const data = 'Case insensitive test'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, 'Case insensitive test')\n\n  await t.completed\n})\n\ntest('should remove content-length header when decompressing', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    const data = 'Test data'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-length'], undefined)\n  t.equal(body, 'Test data')\n\n  await t.completed\n})\n\ntest('should allow decompressing 5xx responses when skipErrorResponses is false', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(500, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    const data = 'Internal server error message'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor({ skipErrorResponses: false }))\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 500)\n  t.equal(response.headers['content-encoding'], undefined) // Should be removed when decompressing\n  t.equal(body, 'Internal server error message') // Should be decompressed\n\n  await t.completed\n})\n\ntest('should allow custom skipStatusCodes', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(201, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    const data = 'Created response'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  // Skip decompression for 201 status codes\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor({ skipStatusCodes: [201, 204, 304] }))\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 201)\n  t.equal(response.headers['content-encoding'], 'gzip') // Should be preserved when skipping\n  t.notEqual(body, 'Created response') // Should still be compressed\n\n  await t.completed\n})\n\ntest('should decompress multiple encodings in correct order', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    // First compress with gzip, then with deflate (gzip, deflate)\n    const gzip = createGzip()\n    const deflate = createDeflate()\n\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip, deflate' // Applied in this order\n    })\n\n    const data = 'Multiple encoding test message'\n\n    // Pipe: data → gzip → deflate → response\n    gzip.pipe(deflate)\n    deflate.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined) // Should be removed\n  t.equal(body, 'Multiple encoding test message') // Should be fully decompressed\n\n  await t.completed\n})\n\ntest('should handle legacy encoding names (x-gzip)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'x-gzip' // Legacy name\n    })\n\n    const data = 'Legacy encoding test'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined) // Should be removed\n  t.equal(body, 'Legacy encoding test')\n\n  await t.completed\n})\n\ntest('should pass through responses with unsupported encoding in chain', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip, unsupported, deflate' // Contains unsupported encoding\n    })\n    res.end('This should pass through unchanged')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], 'gzip, unsupported, deflate') // Should be preserved\n  t.equal(body, 'This should pass through unchanged')\n\n  await t.completed\n})\n\ntest('should handle empty encoding values', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip, ' // Contains empty value at end\n    })\n\n    const data = 'Empty encoding value test'\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-encoding'], undefined)\n  t.equal(body, 'Empty encoding value test')\n\n  await t.completed\n})\n\ntest('should handle multiple pause/resume cycles during decompression', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const data = 'Large data chunk for testing multiple pause/resume cycles. '.repeat(1000)\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n\n    gzip.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  let controller\n  let callCount = 0\n  let responseData = ''\n\n  const handler = {\n    onRequestStart (ctrl) {\n      controller = ctrl\n    },\n\n    onResponseStart (ctrl, statusCode, headers, statusMessage) {\n      t.equal(statusCode, 200)\n\n      for (let i = 0; i < 3; i++) {\n        callCount++\n        controller.pause()\n        controller.resume()\n      }\n    },\n\n    onResponseData (ctrl, chunk) {\n      responseData += chunk.toString()\n    },\n\n    onResponseEnd (ctrl, trailers) {\n      t.equal(callCount, 3, 'Should have called pause/resume 3 times')\n      t.equal(responseData, data, 'All data should be received')\n    },\n\n    onResponseError (ctrl, err) {\n      t.fail(err)\n    }\n  }\n\n  await client.dispatch({\n    method: 'GET',\n    path: '/'\n  }, handler)\n\n  await t.completed\n})\n\ntest('should handle controller pause with chained decompression', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const data = 'Test data for chained decompression pause/resume functionality'\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    const deflate = createDeflate()\n\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip, deflate'\n    })\n\n    gzip.pipe(deflate)\n    deflate.pipe(res)\n    gzip.end(data)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  let controller\n  let pauseResumeWorked = false\n  let responseData = ''\n\n  const handler = {\n    onRequestStart (ctrl) {\n      controller = ctrl\n    },\n\n    onResponseStart (ctrl, statusCode, headers, statusMessage) {\n      t.equal(statusCode, 200)\n\n      try {\n        controller.pause()\n        controller.resume()\n        pauseResumeWorked = true\n      } catch (err) {\n        t.fail('Pause/resume should not throw error')\n      }\n    },\n\n    onResponseData (ctrl, chunk) {\n      responseData += chunk.toString()\n    },\n\n    onResponseEnd (ctrl, trailers) {\n      t.ok(pauseResumeWorked, 'Pause/resume should work with chained decompression')\n      t.equal(responseData, data, 'Data should be correctly decompressed from chained encodings')\n    },\n\n    onResponseError (ctrl, err) {\n      t.fail(err)\n    }\n  }\n\n  await client.dispatch({\n    method: 'GET',\n    path: '/'\n  }, handler)\n\n  await t.completed\n})\n\ntest('should behave like fetch() for compressed responses', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const testData = 'Test data that will be compressed and should be automatically decompressed by both fetch and request with decompress interceptor'\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': 'gzip'\n    })\n    gzip.pipe(res)\n    gzip.end(testData)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const { fetch } = require('../..')\n  const fetchResponse = await fetch(baseUrl)\n  const fetchBody = await fetchResponse.text()\n\n  const client = new Client(baseUrl)\n  const requestResponseWithoutDecompression = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n  const requestBodyWithoutDecompression = await requestResponseWithoutDecompression.body.text()\n\n  const clientWithDecompression = client.compose(createDecompressInterceptor())\n  const requestResponseWithDecompression = await clientWithDecompression.request({\n    method: 'GET',\n    path: '/'\n  })\n  const requestBodyWithDecompression = await requestResponseWithDecompression.body.text()\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  t.equal(fetchResponse.status, 200)\n  t.equal(fetchBody, testData, 'fetch should automatically decompress')\n  t.equal(requestBodyWithDecompression, fetchBody, 'request with decompression interceptor should match fetch behavior')\n  t.notEqual(requestBodyWithoutDecompression, fetchBody, 'request without decompression interceptor should return compressed data')\n  t.equal(fetchResponse.headers.get('content-type'), 'text/plain', 'content-type header should be preserved with fetch')\n  t.equal(fetchResponse.headers.get('content-encoding'), 'gzip', 'content-encoding header should be preserved with fetch')\n  t.equal(requestResponseWithoutDecompression.headers['content-type'], 'text/plain', 'content-type header should be preserved without decompression')\n  t.equal(requestResponseWithoutDecompression.headers['content-encoding'], 'gzip', 'content-encoding header should be preserved without decompression')\n  t.equal(requestResponseWithDecompression.headers['content-type'], 'text/plain', 'content-type header should be preserved with decompression')\n  t.equal(requestResponseWithDecompression.headers['content-encoding'], undefined, 'content-encoding header should be removed with decompression')\n  await t.completed\n})\n\n// CVE fix: Limit the number of content-encodings to prevent resource exhaustion\n// Similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206)\nconst MAX_CONTENT_ENCODINGS = 5\n\ntest(`should allow exactly ${MAX_CONTENT_ENCODINGS} content-encodings`, async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    // Use identity encodings (no actual compression) for simplicity\n    const encodings = Array(MAX_CONTENT_ENCODINGS).fill('identity').join(', ')\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': encodings\n    })\n    res.end('test')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/'\n  })\n\n  // With 5 identity encodings, the interceptor should pass through (identity is not in supportedEncodings)\n  t.equal(response.statusCode, 200)\n  t.ok(response.headers['content-encoding'], 'content-encoding header should be preserved for identity')\n  t.equal(await response.body.text(), 'test')\n\n  await t.completed\n})\n\ntest(`should reject more than ${MAX_CONTENT_ENCODINGS} content-encodings`, async t => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const encodings = Array(MAX_CONTENT_ENCODINGS + 1).fill('gzip').join(', ')\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': encodings\n    })\n    res.end('test')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  try {\n    const response = await client.request({\n      method: 'GET',\n      path: '/'\n    })\n    await response.body.text()\n    t.fail('Should have thrown an error')\n  } catch (err) {\n    t.ok(err.message.includes('content-encoding'), 'Error should mention content-encoding')\n  }\n\n  await t.completed\n})\n\ntest('should reject excessive content-encoding chains', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const encodings = Array(100).fill('gzip').join(', ')\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Content-Encoding': encodings\n    })\n    res.end('test')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(createDecompressInterceptor())\n\n  after(async () => {\n    await client.close()\n    server.close()\n    await once(server, 'close')\n  })\n\n  try {\n    const response = await client.request({\n      method: 'GET',\n      path: '/'\n    })\n    await response.body.text()\n    t.fail('Should have thrown an error')\n  } catch (err) {\n    t.ok(err.message.includes('content-encoding'), 'Error should mention content-encoding')\n  }\n\n  await t.completed\n})\n\ntest('should work with global dispatcher for both fetch() and request()', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const testData = 'Global dispatcher test data for decompression interceptor'\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const gzip = createGzip()\n    const chunks = []\n\n    gzip.on('data', chunk => chunks.push(chunk))\n    gzip.on('end', () => {\n      const compressedData = Buffer.concat(chunks)\n      res.writeHead(200, {\n        'Content-Type': 'text/plain',\n        'Content-Encoding': 'gzip',\n        'Content-Length': compressedData.length\n      })\n      res.end(compressedData)\n    })\n\n    gzip.end(testData)\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const originalDispatcher = getGlobalDispatcher()\n\n  setGlobalDispatcher(getGlobalDispatcher().compose(createDecompressInterceptor()))\n\n  after(async () => {\n    setGlobalDispatcher(originalDispatcher)\n    server.close()\n    await once(server, 'close')\n  })\n\n  const { fetch } = require('../..')\n  const fetchResponse = await fetch(baseUrl)\n  const fetchBody = await fetchResponse.text()\n\n  const requestResponse = await request(baseUrl, {\n    method: 'GET'\n  })\n  const requestBody = await requestResponse.body.text()\n\n  t.equal(fetchResponse.status, 200)\n  t.equal(fetchBody, testData, 'fetch should automatically decompress with global interceptor')\n  t.equal(requestResponse.statusCode, 200)\n  t.equal(requestBody, testData, 'request should automatically decompress with global interceptor')\n  t.equal(requestResponse.headers['content-encoding'], undefined, 'request content-encoding header should be removed with global interceptor')\n  t.equal(requestResponse.headers['content-length'], undefined, 'request content-length header should be removed with global interceptor')\n  t.equal(fetchResponse.headers.get('content-length'), undefined, 'content-length header should be removed with fetch due to global interceptor')\n  t.equal(fetchResponse.headers.get('content-encoding'), undefined, 'content-encoding header should be removed with fetch due to global interceptor')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/interceptors/deduplicate.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { describe, test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { strictEqual } = require('node:assert')\nconst { setTimeout: sleep } = require('node:timers/promises')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client, interceptors } = require('../../index')\n\ndescribe('Deduplicate Interceptor', () => {\n  test('deduplicates concurrent requests for the same resource', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      // Simulate slow response to ensure requests overlap\n      await sleep(100)\n      res.end('response-body')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send multiple concurrent requests\n    const [res1, res2, res3] = await Promise.all([\n      client.request(request),\n      client.request(request),\n      client.request(request)\n    ])\n\n    // Only one request should have reached the origin\n    strictEqual(requestsToOrigin, 1)\n\n    // All responses should have the same body\n    const [body1, body2, body3] = await Promise.all([\n      res1.body.text(),\n      res2.body.text(),\n      res3.body.text()\n    ])\n\n    strictEqual(body1, 'response-body')\n    strictEqual(body2, 'response-body')\n    strictEqual(body3, 'response-body')\n  })\n\n  test('deduplicates concurrent requests with same headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers['accept-encoding']}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        'accept-encoding': 'gzip'\n      }\n    }\n\n    // Send concurrent requests with same header values\n    const [res1, res2] = await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for gzip')\n    strictEqual(body2, 'response for gzip')\n  })\n\n  test('does not deduplicate requests with different header values', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers['accept-encoding']}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const requestGzip = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        'accept-encoding': 'gzip'\n      }\n    }\n\n    const requestBr = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/',\n      headers: {\n        'accept-encoding': 'br'\n      }\n    }\n\n    // Send concurrent requests with different header values\n    const [res1, res2] = await Promise.all([\n      client.request(requestGzip),\n      client.request(requestBr)\n    ])\n\n    // Both should reach origin since they have different header values\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for gzip')\n    strictEqual(body2, 'response for br')\n  })\n\n  test('does not deduplicate requests with different paths', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.url}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests to different paths\n    const [res1, res2] = await Promise.all([\n      client.request({ origin: 'localhost', method: 'GET', path: '/a' }),\n      client.request({ origin: 'localhost', method: 'GET', path: '/b' })\n    ])\n\n    // Both should reach origin\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for /a')\n    strictEqual(body2, 'response for /b')\n  })\n\n  test('propagates errors to all waiting handlers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(50)\n      // Destroy the connection to simulate an error\n      res.destroy()\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send concurrent requests that will all fail\n    const results = await Promise.allSettled([\n      client.request(request),\n      client.request(request),\n      client.request(request)\n    ])\n\n    // Only one request should have reached the origin\n    strictEqual(requestsToOrigin, 1)\n\n    // All should have failed\n    strictEqual(results[0].status, 'rejected')\n    strictEqual(results[1].status, 'rejected')\n    strictEqual(results[2].status, 'rejected')\n  })\n\n  test('works with cache interceptor for cacheable responses', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.setHeader('cache-control', 's-maxage=10')\n      res.end('cached-response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // First batch of concurrent requests\n    await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n    strictEqual(requestsToOrigin, 1)\n\n    // Subsequent request should be from cache\n    const res = await client.request(request)\n    strictEqual(requestsToOrigin, 1)\n    strictEqual(await res.body.text(), 'cached-response')\n  })\n\n  test('deduplication works with non-cacheable responses', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.setHeader('cache-control', 'no-store')\n      res.end('non-cached-response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n      .compose(interceptors.cache())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Concurrent requests for non-cacheable response should still be deduplicated\n    const [res1, res2] = await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'non-cached-response')\n    strictEqual(body2, 'non-cached-response')\n\n    // But subsequent requests should NOT be cached\n    const res3 = await client.request(request)\n    strictEqual(requestsToOrigin, 2)\n    strictEqual(await res3.body.text(), 'non-cached-response')\n  })\n\n  test('deduplication cleans up pending requests after completion', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(50)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // First batch\n    await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n    strictEqual(requestsToOrigin, 1)\n\n    // Second batch - should create new request since first batch is complete\n    await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n    strictEqual(requestsToOrigin, 2)\n  })\n\n  test('deduplication works with chunked response bodies', async () => {\n    let requestsToOrigin = 0\n    const bodyPart = 'chunk-data-'\n\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      res.setHeader('transfer-encoding', 'chunked')\n      // Send multiple chunks\n      for (let i = 0; i < 5; i++) {\n        res.write(bodyPart + i)\n        await sleep(10)\n      }\n      res.end()\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send concurrent requests\n    const [res1, res2] = await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n\n    strictEqual(requestsToOrigin, 1)\n\n    const expectedBody = 'chunk-data-0chunk-data-1chunk-data-2chunk-data-3chunk-data-4'\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, expectedBody)\n    strictEqual(body2, expectedBody)\n  })\n\n  test('all response properties are available to deduplicated handlers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.setHeader('x-custom-header', 'custom-value')\n      res.statusCode = 201\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    const [res1, res2] = await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n\n    strictEqual(requestsToOrigin, 1)\n\n    // Both responses should have the same status code and headers\n    strictEqual(res1.statusCode, 201)\n    strictEqual(res2.statusCode, 201)\n    strictEqual(res1.headers['x-custom-header'], 'custom-value')\n    strictEqual(res2.headers['x-custom-header'], 'custom-value')\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response')\n    strictEqual(body2, 'response')\n  })\n\n  test('diagnostic channel tracks pending requests correctly', async () => {\n    const events = []\n    const channel = diagnosticsChannel.channel('undici:request:pending-requests')\n\n    const onMessage = (message) => {\n      events.push(message)\n    }\n    channel.subscribe(onMessage)\n\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      await sleep(100)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      channel.unsubscribe(onMessage)\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send concurrent requests\n    await Promise.all([\n      client.request(request),\n      client.request(request)\n    ])\n\n    // Should have seen: added (size=1), removed (size=0)\n    strictEqual(events.length, 2)\n    strictEqual(events[0].type, 'added')\n    strictEqual(events[0].size, 1)\n    strictEqual(events[1].type, 'removed')\n    strictEqual(events[1].size, 0)\n  })\n\n  test('diagnostic channel shows cleanup after error', async () => {\n    const events = []\n    const channel = diagnosticsChannel.channel('undici:request:pending-requests')\n\n    const onMessage = (message) => {\n      events.push(message)\n    }\n    channel.subscribe(onMessage)\n\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      await sleep(50)\n      res.destroy() // Simulate error\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      channel.unsubscribe(onMessage)\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    // Send concurrent requests that will error\n    await Promise.allSettled([\n      client.request(request),\n      client.request(request)\n    ])\n\n    // Should have seen: added (size=1), removed (size=0)\n    strictEqual(events.length, 2)\n    strictEqual(events[0].type, 'added')\n    strictEqual(events[0].size, 1)\n    strictEqual(events[1].type, 'removed')\n    strictEqual(events[1].size, 0)\n  })\n\n  test('diagnostic channel tracks multiple pending requests separately', async () => {\n    const events = []\n    const channel = diagnosticsChannel.channel('undici:request:pending-requests')\n\n    const onMessage = (message) => {\n      events.push(message)\n    }\n    channel.subscribe(onMessage)\n\n    let requestsToOrigin = 0\n\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.url}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      channel.unsubscribe(onMessage)\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send requests to two different paths concurrently\n    await Promise.all([\n      client.request({ origin: 'localhost', method: 'GET', path: '/a' }),\n      client.request({ origin: 'localhost', method: 'GET', path: '/b' }),\n      client.request({ origin: 'localhost', method: 'GET', path: '/a' }), // Deduplicated with first\n      client.request({ origin: 'localhost', method: 'GET', path: '/b' })  // Deduplicated with second\n    ])\n\n    // Should have 2 origin requests (one for /a, one for /b)\n    strictEqual(requestsToOrigin, 2)\n\n    // Should have 4 events: 2 added, 2 removed\n    strictEqual(events.length, 4)\n\n    // First two should be 'added' events with sizes 1 and 2\n    const addedEvents = events.filter(e => e.type === 'added')\n    const removedEvents = events.filter(e => e.type === 'removed')\n\n    strictEqual(addedEvents.length, 2)\n    strictEqual(removedEvents.length, 2)\n    strictEqual(addedEvents[0].size, 1)\n    strictEqual(addedEvents[1].size, 2)\n    strictEqual(removedEvents[removedEvents.length - 1].size, 0) // All cleaned up\n  })\n\n  test('does not deduplicate requests with different Authorization headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers.authorization}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different Authorization headers\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { authorization: 'Bearer token-user-1' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { authorization: 'Bearer token-user-2' }\n      })\n    ])\n\n    // Both requests should reach origin since they have different Authorization headers\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for Bearer token-user-1')\n    strictEqual(body2, 'response for Bearer token-user-2')\n  })\n\n  test('does not deduplicate requests with different Cookie headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers.cookie}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different Cookie headers\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { cookie: 'session=user1-session-id' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { cookie: 'session=user2-session-id' }\n      })\n    ])\n\n    // Both requests should reach origin since they have different Cookie headers\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for session=user1-session-id')\n    strictEqual(body2, 'response for session=user2-session-id')\n  })\n\n  test('deduplicates requests with same Authorization header', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers.authorization}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with the same Authorization header\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { authorization: 'Bearer same-token' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { authorization: 'Bearer same-token' }\n      })\n    ])\n\n    // Only one request should reach origin since they have the same Authorization header\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for Bearer same-token')\n    strictEqual(body2, 'response for Bearer same-token')\n  })\n\n  test('skipHeaderNames skips deduplication for requests with specified headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response ${requestsToOrigin}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ skipHeaderNames: ['x-no-dedupe'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with the skip header\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-no-dedupe': 'true' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-no-dedupe': 'true' }\n      })\n    ])\n\n    // Both requests should reach origin since they have the skip header\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response 1')\n    strictEqual(body2, 'response 2')\n  })\n\n  test('skipHeaderNames is case-insensitive', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response ${requestsToOrigin}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ skipHeaderNames: ['X-No-Dedupe'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with lowercase header (should still match)\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-no-dedupe': 'true' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-no-dedupe': 'true' }\n      })\n    ])\n\n    // Both requests should reach origin since header matching is case-insensitive\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response 1')\n    strictEqual(body2, 'response 2')\n  })\n\n  test('skipHeaderNames allows deduplication for requests without specified headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ skipHeaderNames: ['x-no-dedupe'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests without the skip header\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      })\n    ])\n\n    // Only one request should reach origin since they don't have the skip header\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response')\n    strictEqual(body2, 'response')\n  })\n\n  test('skipHeaderNames with multiple headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response ${requestsToOrigin}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ skipHeaderNames: ['x-skip-1', 'x-skip-2'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different skip headers\n    const [res1, res2, res3] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-skip-1': 'true' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-skip-2': 'true' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/'\n      })\n    ])\n\n    // First two should not be deduplicated (they have skip headers)\n    // Third request starts a new pending request (no skip header)\n    // If third request arrives after first two start, it won't be deduplicated with them\n    // but could be deduplicated with other requests without skip headers\n    strictEqual(requestsToOrigin, 3)\n\n    const [body1, body2, body3] = await Promise.all([\n      res1.body.text(),\n      res2.body.text(),\n      res3.body.text()\n    ])\n\n    strictEqual(body1, 'response 1')\n    strictEqual(body2, 'response 2')\n    strictEqual(body3, 'response 3')\n  })\n\n  test('throws TypeError if skipHeaderNames is not an array', () => {\n    const { throws } = require('node:assert')\n    throws(() => {\n      interceptors.deduplicate({ skipHeaderNames: 'not-an-array' })\n    }, {\n      name: 'TypeError',\n      message: 'expected opts.skipHeaderNames to be an array, got string'\n    })\n  })\n\n  test('excludeHeaderNames deduplicates requests with different excluded header values', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ excludeHeaderNames: ['x-request-id'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different x-request-id values\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-1' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-2' }\n      })\n    ])\n\n    // Only one request should reach origin since x-request-id is excluded from the key\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response')\n    strictEqual(body2, 'response')\n  })\n\n  test('excludeHeaderNames is case-insensitive', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ excludeHeaderNames: ['X-Request-ID'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with lowercase header (should still be excluded)\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-1' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-2' }\n      })\n    ])\n\n    // Should be deduplicated since header matching is case-insensitive\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response')\n    strictEqual(body2, 'response')\n  })\n\n  test('excludeHeaderNames does not affect other headers in deduplication key', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response for ${req.headers['accept-encoding']}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ excludeHeaderNames: ['x-request-id'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different accept-encoding (not excluded)\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-1', 'accept-encoding': 'gzip' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-2', 'accept-encoding': 'br' }\n      })\n    ])\n\n    // Both should reach origin since accept-encoding differs and is not excluded\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response for gzip')\n    strictEqual(body2, 'response for br')\n  })\n\n  test('excludeHeaderNames with multiple headers', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end('response')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ excludeHeaderNames: ['x-request-id', 'x-correlation-id'] }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    // Send concurrent requests with different values for both excluded headers\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-1', 'x-correlation-id': 'corr-1' }\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'GET',\n        path: '/',\n        headers: { 'x-request-id': 'req-2', 'x-correlation-id': 'corr-2' }\n      })\n    ])\n\n    // Should be deduplicated since both varying headers are excluded\n    strictEqual(requestsToOrigin, 1)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response')\n    strictEqual(body2, 'response')\n  })\n\n  test('does not deduplicate non-safe methods when methods option uses defaults', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      await sleep(100)\n      res.end(`response ${requestsToOrigin}`)\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const [res1, res2] = await Promise.all([\n      client.request({\n        origin: 'localhost',\n        method: 'POST',\n        path: '/',\n        headers: { 'content-type': 'application/json' },\n        body: JSON.stringify({ token: 'a' })\n      }),\n      client.request({\n        origin: 'localhost',\n        method: 'POST',\n        path: '/',\n        headers: { 'content-type': 'application/json' },\n        body: JSON.stringify({ token: 'b' })\n      })\n    ])\n\n    strictEqual(requestsToOrigin, 2)\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(body1, 'response 1')\n    strictEqual(body2, 'response 2')\n  })\n\n  test('does not deduplicate requests that arrive after body streaming starts', async () => {\n    let requestsToOrigin = 0\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n      res.write('chunk-1')\n      await sleep(100)\n      res.end('chunk-2')\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate())\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    const firstResponsePromise = client.request(request)\n\n    // Wait until the first response starts streaming body data.\n    await sleep(20)\n\n    const [res1, res2] = await Promise.all([\n      firstResponsePromise,\n      client.request(request)\n    ])\n\n    const [body1, body2] = await Promise.all([\n      res1.body.text(),\n      res2.body.text()\n    ])\n\n    strictEqual(requestsToOrigin, 2)\n    strictEqual(body1, 'chunk-1chunk-2')\n    strictEqual(body2, 'chunk-1chunk-2')\n  })\n\n  test('errors paused waiting handlers when buffered data exceeds maxBufferSize', async () => {\n    let requestsToOrigin = 0\n    const chunk = Buffer.alloc(8 * 1024, 'a')\n    const totalChunks = 6\n\n    const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n      requestsToOrigin++\n\n      for (let i = 0; i < totalChunks; i++) {\n        res.write(chunk)\n        await sleep(10)\n      }\n\n      res.end()\n    }).listen(0)\n\n    const client = new Client(`http://localhost:${server.address().port}`)\n      .compose(interceptors.deduplicate({ maxBufferSize: 16 * 1024 }))\n\n    after(async () => {\n      server.close()\n      await client.close()\n    })\n\n    await once(server, 'listening')\n\n    const request = {\n      origin: 'localhost',\n      method: 'GET',\n      path: '/'\n    }\n\n    const primaryResponsePromise = client.request(request)\n\n    const slowWaitingHandlerErrorPromise = new Promise((resolve, reject) => {\n      client.dispatch(request, {\n        onConnect () {},\n        onHeaders () {},\n        onData () {\n          // Pause the waiting handler immediately and never resume it.\n          return false\n        },\n        onComplete () {\n          reject(new Error('Expected paused waiting handler to fail'))\n        },\n        onError (err) {\n          resolve(err)\n        }\n      })\n    })\n\n    const primaryResponse = await primaryResponsePromise\n    const primaryBody = await primaryResponse.body.arrayBuffer()\n    const waitingHandlerErr = await slowWaitingHandlerErrorPromise\n\n    strictEqual(requestsToOrigin, 1)\n    strictEqual(primaryBody.byteLength, chunk.length * totalChunks)\n    strictEqual(waitingHandlerErr.code, 'UND_ERR_ABORTED')\n  })\n\n  test('throws TypeError if maxBufferSize is not a positive finite number', () => {\n    const { throws } = require('node:assert')\n    throws(() => {\n      interceptors.deduplicate({ maxBufferSize: 0 })\n    }, {\n      name: 'TypeError',\n      message: 'expected opts.maxBufferSize to be a positive finite number, got 0'\n    })\n  })\n\n  test('throws TypeError if excludeHeaderNames is not an array', () => {\n    const { throws } = require('node:assert')\n    throws(() => {\n      interceptors.deduplicate({ excludeHeaderNames: 'not-an-array' })\n    }, {\n      name: 'TypeError',\n      message: 'expected opts.excludeHeaderNames to be an array, got string'\n    })\n  })\n})\n"
  },
  {
    "path": "test/interceptors/dns.js",
    "content": "'use strict'\n\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { test, after } = require('node:test')\nconst { isIP } = require('node:net')\nconst { lookup } = require('node:dns')\nconst { createServer } = require('node:http')\nconst { createServer: createSecureServer } = require('node:https')\nconst { once } = require('node:events')\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst pem = require('@metcoder95/https-pem')\n\nconst { interceptors, Agent, request } = require('../..')\nconst { dns } = interceptors\n\ntest('Should validate options', t => {\n  t = tspl(t, { plan: 11 })\n\n  t.throws(() => dns({ dualStack: 'true' }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ dualStack: 0 }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ affinity: '4' }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ affinity: 7 }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ maxTTL: -1 }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ maxTTL: '0' }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ maxItems: '1' }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ maxItems: -1 }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ lookup: {} }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ pick: [] }), { code: 'UND_ERR_INVALID_ARG' })\n  t.throws(() => dns({ storage: new Map() }), { code: 'UND_ERR_INVALID_ARG' })\n})\n\ntest('Should automatically resolve IPs (dual stack)', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const hostsnames = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        const url = new URL(opts.origin)\n\n        t.equal(hostsnames.includes(url.hostname), false)\n\n        if (url.hostname[0] === '[') {\n          // [::1] -> ::1\n          t.equal(isIP(url.hostname.slice(1, 4)), 6)\n        } else {\n          t.equal(isIP(url.hostname), 4)\n        }\n\n        hostsnames.push(url.hostname)\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should respect DNS origin hostname for SNI on TLS', async t => {\n  t = tspl(t, { plan: 12 })\n\n  const hostsnames = []\n  const server = createSecureServer(pem)\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    t.equal(req.headers.host, `localhost:${server.address().port}`)\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent({\n    connect: {\n      rejectUnauthorized: false\n    }\n  }).compose([\n    dispatch => {\n      return (opts, handler) => {\n        const url = new URL(opts.origin)\n\n        t.equal(hostsnames.includes(url.hostname), false)\n        t.equal(opts.servername, 'localhost')\n\n        if (url.hostname[0] === '[') {\n          // [::1] -> ::1\n          t.equal(isIP(url.hostname.slice(1, 4)), 6)\n        } else {\n          t.equal(isIP(url.hostname), 4)\n        }\n\n        hostsnames.push(url.hostname)\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `https://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `https://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should recover on network errors (dual stack - 4)', async t => {\n  t = tspl(t, { plan: 7 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0, '::1')\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 3:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should recover on network errors (dual stack - 6)', async t => {\n  t = tspl(t, { plan: 7 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0, '127.0.0.1')\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 3:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname), 4)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should throw when on dual-stack disabled (4)', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({ dualStack: false, affinity: 4 })\n  ])\n\n  const promise = client.request({\n    ...requestOptions,\n    origin: 'http://localhost:1234'\n  })\n\n  await t.rejects(promise, 'ECONNREFUSED')\n\n  await t.completed\n})\n\ntest('Should throw when on dual-stack disabled (6)', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({ dualStack: false, affinity: 6 })\n  ])\n\n  const promise = client.request({\n    ...requestOptions,\n    origin: 'http://localhost:9999'\n  })\n\n  await t.rejects(promise, 'ECONNREFUSED')\n\n  await t.completed\n})\n\ntest('Should automatically resolve IPs (dual stack disabled - 4)', async t => {\n  t = tspl(t, { plan: 6 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname), 4)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({ dualStack: false })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should automatically resolve IPs (dual stack disabled - 6)', async t => {\n  t = tspl(t, { plan: 6 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({ dualStack: false, affinity: 6 })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('Should we handle TTL (4)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const clock = FakeTimers.install()\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0, '127.0.0.1')\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 3:\n            t.equal(isIP(url.hostname), 4)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      dualStack: false,\n      affinity: 4,\n      maxTTL: 400,\n      lookup: (origin, opts, cb) => {\n        ++lookupCounter\n        lookup(\n          origin.hostname,\n          { all: true, family: opts.affinity },\n          cb\n        )\n      }\n    })\n  ])\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(200)\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  clock.tick(300)\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 2)\n})\n\ntest('Should we handle TTL (6)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const clock = FakeTimers.install()\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0, '::1')\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 3:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      dualStack: false,\n      affinity: 6,\n      maxTTL: 400,\n      lookup: (origin, opts, cb) => {\n        ++lookupCounter\n        lookup(\n          origin.hostname,\n          { all: true, family: opts.affinity },\n          cb\n        )\n      }\n    })\n  ])\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(200)\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  clock.tick(300)\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n  t.equal(lookupCounter, 2)\n})\n\ntest('Should set lowest TTL between resolved and option maxTTL', async t => {\n  t = tspl(t, { plan: 9 })\n\n  const clock = FakeTimers.install()\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0, '127.0.0.1')\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose(\n    dns({\n      dualStack: false,\n      affinity: 4,\n      maxTTL: 200,\n      lookup: (origin, opts, cb) => {\n        ++lookupCounter\n        cb(null, [\n          {\n            address: '127.0.0.1',\n            family: 4,\n            ttl: lookupCounter === 1 ? 50 : 500\n          }\n        ])\n      }\n    })\n  )\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(100)\n\n  // 100ms: lookup since ttl = Math.min(50, maxTTL: 200)\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  clock.tick(100)\n\n  // 100ms: cached since ttl = Math.min(500, maxTTL: 200)\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  clock.tick(150)\n\n  // 250ms: lookup since ttl = Math.min(500, maxTTL: 200)\n  const response4 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response4.statusCode, 200)\n  t.equal(await response4.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 3)\n})\n\ntest('Should use all dns entries (dual stack)', async t => {\n  t = tspl(t, { plan: 16 })\n\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n        switch (counter) {\n          case 1:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n\n          case 2:\n            t.equal(url.hostname, '[::1]')\n            break\n\n          case 3:\n            t.equal(url.hostname, '2.2.2.2')\n            break\n\n          case 4:\n            t.equal(url.hostname, '[::2]')\n            break\n\n          case 5:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        url.hostname = '127.0.0.1'\n        opts.origin = url.toString()\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '::1', family: 6 },\n          { address: '::2', family: 6 },\n          { address: '1.1.1.1', family: 4 },\n          { address: '2.2.2.2', family: 4 }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  for (let i = 0; i < 5; i++) {\n    const response = await client.request({\n      ...requestOptions,\n      origin: `http://localhost:${server.address().port}`\n    })\n\n    t.equal(response.statusCode, 200)\n    t.equal(await response.body.text(), 'hello world!')\n  }\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should use all dns entries (dual stack disabled - 4)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n\n          case 2:\n            t.equal(url.hostname, '2.2.2.2')\n            break\n\n          case 3:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        url.hostname = '127.0.0.1'\n        opts.origin = url.toString()\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      dualStack: false,\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '1.1.1.1', family: 4 },\n          { address: '2.2.2.2', family: 4 }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response1 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response1.statusCode, 200)\n  t.equal(await response1.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should use all dns entries (dual stack disabled - 6)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(url.hostname, '[::1]')\n            break\n\n          case 2:\n            t.equal(url.hostname, '[::2]')\n            break\n\n          case 3:\n            t.equal(url.hostname, '[::1]')\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        url.hostname = '127.0.0.1'\n        opts.origin = url.toString()\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      dualStack: false,\n      affinity: 6,\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '::1', family: 6 },\n          { address: '::2', family: 6 }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response1 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response1.statusCode, 200)\n  t.equal(await response1.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should handle single family resolved (dual stack)', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const clock = FakeTimers.install()\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        if (lookupCounter === 1) {\n          cb(null, [\n            { address: '127.0.0.1', family: 4, ttl: 50 }\n          ])\n        } else {\n          cb(null, [\n            { address: '::1', family: 6, ttl: 50 }\n          ])\n        }\n      }\n    })\n  ])\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(100)\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 2)\n})\n\ntest('Should prefer affinity (dual stack - 4)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const clock = FakeTimers.install()\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n\n          case 2:\n            t.equal(url.hostname, '2.2.2.2')\n            break\n\n          case 3:\n            t.equal(url.hostname, '1.1.1.1')\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        url.hostname = '127.0.0.1'\n        opts.origin = url.toString()\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      affinity: 4,\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '1.1.1.1', family: 4 },\n          { address: '2.2.2.2', family: 4 },\n          { address: '::1', family: 6 },\n          { address: '::2', family: 6 }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(100)\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should prefer affinity (dual stack - 6)', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const clock = FakeTimers.install()\n  let counter = 0\n  let lookupCounter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(url.hostname, '[::1]')\n            break\n\n          case 2:\n            t.equal(url.hostname, '[::2]')\n            break\n\n          case 3:\n            t.equal(url.hostname, '[::1]')\n            break\n          default:\n            t.fail('should not reach this point')\n        }\n\n        url.hostname = '127.0.0.1'\n        opts.origin = url.toString()\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      affinity: 6,\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '1.1.1.1', family: 4 },\n          { address: '2.2.2.2', family: 4 },\n          { address: '::1', family: 6 },\n          { address: '::2', family: 6 }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    clock.uninstall()\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  clock.tick(100)\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world!')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should use resolved ports (4)', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let lookupCounter = 0\n  const server1 = createServer({ joinDuplicateHeaders: true })\n  const server2 = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server1.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server1.listen(0)\n\n  server2.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world! (x2)')\n  })\n  server2.listen(0)\n\n  await Promise.all([once(server1, 'listening'), once(server2, 'listening')])\n\n  const client = new Agent().compose([\n    dns({\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '127.0.0.1', family: 4, port: server1.address().port },\n          { address: '127.0.0.1', family: 4, port: server2.address().port }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server1.close()\n    server2.close()\n\n    await Promise.all([once(server1, 'close'), once(server2, 'close')])\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world! (x2)')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should use resolved ports (6)', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let lookupCounter = 0\n  const server1 = createServer({ joinDuplicateHeaders: true })\n  const server2 = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server1.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server1.listen(0, '::1')\n\n  server2.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world! (x2)')\n  })\n  server2.listen(0, '::1')\n\n  await Promise.all([once(server1, 'listening'), once(server2, 'listening')])\n\n  const client = new Agent().compose([\n    dns({\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        cb(null, [\n          { address: '::1', family: 6, port: server1.address().port },\n          { address: '::1', family: 6, port: server2.address().port }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server1.close()\n    server2.close()\n\n    await Promise.all([once(server1, 'close'), once(server2, 'close')])\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world! (x2)')\n\n  t.equal(lookupCounter, 1)\n})\n\ntest('Should handle max cached items', async t => {\n  t = tspl(t, { plan: 9 })\n\n  let counter = 0\n  const server1 = createServer({ joinDuplicateHeaders: true })\n  const server2 = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server1.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server1.listen(0)\n\n  server2.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world! (x2)')\n  })\n  server2.listen(0)\n\n  await Promise.all([once(server1, 'listening'), once(server2, 'listening')])\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 3:\n            t.equal(url.hostname, 'developer.mozilla.org')\n            // Rewrite origin to avoid reaching internet\n            opts.origin = `http://127.0.0.1:${server2.address().port}`\n            break\n          default:\n            t.fails('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      maxItems: 1,\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server1.close()\n    server2.close()\n\n    await Promise.all([once(server1, 'close'), once(server2, 'close')])\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server1.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server1.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: 'https://developer.mozilla.org'\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world! (x2)')\n})\n\ntest('Should support external storage', async t => {\n  t = tspl(t, { plan: 9 })\n\n  let counter = 0\n  const server1 = createServer({ joinDuplicateHeaders: true })\n  const server2 = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server1.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server1.listen(0)\n\n  server2.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world! (x2)')\n  })\n  server2.listen(0)\n\n  await Promise.all([once(server1, 'listening'), once(server2, 'listening')])\n\n  const cache = new Map()\n  const storage = {\n    get (origin) {\n      return cache.get(origin)\n    },\n    set (origin, records) {\n      cache.set(origin, records)\n    },\n    delete (origin) {\n      cache.delete(origin)\n    },\n    // simulate internal DNSStorage behaviour with `maxItems: 1` parameter\n    full () {\n      return cache.size === 1\n    }\n  }\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        ++counter\n        const url = new URL(opts.origin)\n\n        switch (counter) {\n          case 1:\n            t.equal(isIP(url.hostname), 4)\n            break\n\n          case 2:\n            // [::1] -> ::1\n            t.equal(isIP(url.hostname.slice(1, 4)), 6)\n            break\n\n          case 3:\n            t.equal(url.hostname, 'developer.mozilla.org')\n            // Rewrite origin to avoid reaching internet\n            opts.origin = `http://127.0.0.1:${server2.address().port}`\n            break\n          default:\n            t.fails('should not reach this point')\n        }\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      storage,\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server1.close()\n    server2.close()\n\n    await Promise.all([once(server1, 'close'), once(server2, 'close')])\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server1.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server1.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n\n  const response3 = await client.request({\n    ...requestOptions,\n    origin: 'https://developer.mozilla.org'\n  })\n\n  t.equal(response3.statusCode, 200)\n  t.equal(await response3.body.text(), 'hello world! (x2)')\n})\n\ntest('retry once with dual-stack', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  let counter = 0\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        counter++\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '127.0.0.1',\n            port: 3669,\n            family: 4,\n            ttl: 1000\n          },\n          {\n            address: '::1',\n            port: 3669,\n            family: 6,\n            ttl: 1000\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n  })\n\n  await t.rejects(client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  }), 'ECONNREFUSED')\n\n  t.equal(counter, 2)\n})\n\ntest('Should handle ENOTFOUND response error', async t => {\n  t = tspl(t, { plan: 3 })\n  let lookupCounter = 0\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    origin: 'http://localhost'\n  }\n\n  const client = new Agent().compose([\n    dns({\n      lookup (origin, opts, cb) {\n        lookupCounter++\n        if (lookupCounter === 1) {\n          const err = new Error('test error')\n          err.code = 'ENOTFOUND'\n          cb(err)\n        } else {\n          // Causes InformationalError\n          cb(null, [])\n        }\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n  })\n\n  let error1\n  try {\n    await client.request(requestOptions)\n  } catch (err) {\n    error1 = err\n  }\n  t.equal(error1.code, 'ENOTFOUND')\n\n  // Test that the records in the dns interceptor were deleted after the\n  // previous request\n  let error2\n  try {\n    await client.request(requestOptions)\n  } catch (err) {\n    error2 = err\n  }\n  t.equal(error2.name, 'InformationalError')\n\n  t.equal(lookupCounter, 2)\n})\n\ntest('#3937 - Handle host correctly', async t => {\n  t = tspl(t, { plan: 10 })\n\n  const hostsnames = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    t.equal(req.headers.host, `localhost:${server.address().port}`)\n\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dispatch => {\n      return (opts, handler) => {\n        const url = new URL(opts.origin)\n\n        t.equal(hostsnames.includes(url.hostname), false)\n\n        if (url.hostname[0] === '[') {\n          // [::1] -> ::1\n          t.equal(isIP(url.hostname.slice(1, 4)), 6)\n        } else {\n          t.equal(isIP(url.hostname), 4)\n        }\n\n        hostsnames.push(url.hostname)\n\n        return dispatch(opts, handler)\n      }\n    },\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n\n  const response2 = await client.request({\n    ...requestOptions,\n    origin: `http://localhost:${server.address().port}`\n  })\n\n  t.equal(response2.statusCode, 200)\n  t.equal(await response2.body.text(), 'hello world!')\n})\n\ntest('#4444 - Should preserve tuple-style headers', async t => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    t.equal(req.headers.host, `localhost:${server.address().port}`)\n    t.equal(req.headers.foo, 'bar')\n    t.equal(req.headers['0'], undefined)\n\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await request(`http://localhost:${server.address().port}`, {\n    dispatcher: client,\n    headers: [['foo', 'bar']]\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('#4444 - Should preserve iterable headers', async t => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    t.equal(req.headers.host, `localhost:${server.address().port}`)\n    t.equal(req.headers.foo, 'bar')\n    t.equal(req.headers['0'], undefined)\n\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Agent().compose([\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(null, [\n          {\n            address: '::1',\n            family: 6\n          },\n          {\n            address: '127.0.0.1',\n            family: 4\n          }\n        ])\n      }\n    })\n  ])\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await request(`http://localhost:${server.address().port}`, {\n    dispatcher: client,\n    headers: new Map([['foo', 'bar']])\n  })\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('#3951 - Should handle lookup errors correctly', async t => {\n  const suite = tspl(t, { plan: 1 })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  const client = new Agent().compose([\n    dns({\n      lookup: (_origin, _opts, cb) => {\n        cb(new Error('lookup error'))\n      }\n    })\n  ])\n\n  suite.rejects(client.request({\n    ...requestOptions,\n    origin: 'http://localhost'\n  }), new Error('lookup error'))\n})\n"
  },
  {
    "path": "test/interceptors/dump-interceptor.js",
    "content": "'use strict'\nconst { platform } = require('node:os')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst { Client, Agent, interceptors } = require('../..')\nconst { dump } = interceptors\n\n// TODO: Fix tests on windows\nconst skip = platform() === 'win32'\n\ntest('Should handle preemptive network error', { skip }, async t => {\n  t = tspl(t, { plan: 4 })\n  let offset = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const max = 1024 * 1024\n    const buffer = Buffer.alloc(max)\n\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n\n    const interval = setInterval(() => {\n      offset += 256\n      const chunk = buffer.subarray(offset - 256, offset)\n\n      if (offset === max) {\n        clearInterval(interval)\n        res.end(chunk)\n        return\n      }\n\n      res.write(chunk)\n    }, 0)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  const client = new Agent().compose(dump({ maxSize: 1024 * 1024 }))\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  try {\n    await client.request({\n      origin: 'http://localhost',\n      ...requestOptions\n    })\n  } catch (error) {\n    t.equal(error.code, 'ECONNREFUSED')\n  }\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const response = await client.request({\n    origin: `http://localhost:${server.address().port}`,\n    ...requestOptions\n  })\n  const body = await response.body.text()\n\n  t.equal(response.headers['content-length'], `${1024 * 1024}`)\n  t.equal(response.statusCode, 200)\n  t.equal(body, '')\n\n  await t.completed\n})\n\ntest('Should dump on abort', { skip }, async t => {\n  t = tspl(t, { plan: 2 })\n  let offset = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const max = 1024 * 1024\n    const buffer = Buffer.alloc(max)\n\n    res.writeHead(200, {\n      'Content-Type': 'application/octet-stream'\n    })\n\n    const interval = setInterval(() => {\n      offset += 256\n      const chunk = buffer.subarray(offset - 256, offset)\n\n      if (offset === max) {\n        clearInterval(interval)\n        res.end(chunk)\n        return\n      }\n\n      res.write(chunk)\n    }, 0)\n  })\n\n  const abc = new AbortController()\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    signal: abc.signal\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump({ maxSize: 512 }))\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  abc.abort()\n\n  try {\n    await response.body.text()\n  } catch (error) {\n    t.equal(response.statusCode, 200)\n    t.equal(error.name, 'AbortError')\n  }\n\n  await t.completed\n})\n\ntest('Should dump on already aborted request', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n  let offset = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const max = 1024\n    const buffer = Buffer.alloc(max)\n\n    res.writeHead(200, {\n      'Content-Type': 'application/octet-stream'\n    })\n\n    res.once('close', () => {\n      t.equal(offset, 1024)\n    })\n\n    const interval = setInterval(() => {\n      offset += 256\n      const chunk = buffer.subarray(offset - 256, offset)\n\n      if (offset === max) {\n        clearInterval(interval)\n        res.end(chunk)\n        return\n      }\n\n      res.write(chunk)\n    }, 0)\n  })\n\n  const abc = new AbortController()\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    signal: abc.signal\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump({ maxSize: 512 }))\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  abc.abort()\n  client.request(requestOptions).catch(err => {\n    t.equal(err.name, 'AbortError')\n    t.equal(err.message, 'This operation was aborted')\n  })\n\n  await t.completed\n})\n\ntest('Should dump response body up to limit (default)', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const buffer = Buffer.alloc(1024 * 1024)\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n\n    res.end(buffer)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump())\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-length'], `${1024 * 1024}`)\n  t.equal(body, '')\n\n  await t.completed\n})\n\ntest('Should dump response body up to limit and ignore trailers', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Transfer-Encoding': 'chunked',\n      Trailer: 'X-Foo'\n    })\n\n    res.write(Buffer.alloc(1024 * 1024).toString('utf-8'))\n    res.addTrailers({ 'X-Foo': 'bar' })\n    res.end()\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump())\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(body, '')\n  t.equal(response.trailers['x-foo'], undefined)\n\n  await t.completed\n})\n\ntest('Should forward common error', { skip }, async t => {\n  t = tspl(t, { plan: 1 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump())\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  await t.rejects(client.request.bind(client, requestOptions), {\n    name: 'SocketError',\n    code: 'UND_ERR_SOCKET',\n    message: 'other side closed'\n  })\n\n  await t.completed\n})\n\ntest('Should throw on bad opts', { skip }, async t => {\n  t = tspl(t, { plan: 6 })\n\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump({ maxSize: {} })).dispatch(\n        {\n          method: 'GET',\n          path: '/'\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump({ maxSize: '0' })).dispatch(\n        {\n          method: 'GET',\n          path: '/'\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump({ maxSize: -1 })).dispatch(\n        {\n          method: 'GET',\n          path: '/'\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump()).dispatch(\n        {\n          method: 'GET',\n          path: '/',\n          dumpMaxSize: {}\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump()).dispatch(\n        {\n          method: 'GET',\n          path: '/',\n          dumpMaxSize: '0'\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n  t.throws(\n    () => {\n      new Client('http://localhost').compose(dump()).dispatch(\n        {\n          method: 'GET',\n          path: '/',\n          dumpMaxSize: -1\n        },\n        {}\n      )\n    },\n    {\n      name: 'InvalidArgumentError',\n      message: 'maxSize must be a number greater than 0'\n    }\n  )\n})\n\ntest('Should dump response body up to limit (opts)', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const buffer = Buffer.alloc(1 * 1024)\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n    res.end(buffer)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump({ maxSize: 1 * 1024 }))\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-length'], `${1 * 1024}`)\n  t.equal(body, '')\n\n  await t.completed\n})\n\ntest('Should abort if content length grater than max size', { skip }, async t => {\n  t = tspl(t, { plan: 1 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const buffer = Buffer.alloc(2 * 1024)\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n    res.end(buffer)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump({ maxSize: 1 * 1024, abortOnDumped: false }))\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  t.rejects(client.request(requestOptions), {\n    name: 'AbortError',\n    message: 'Response size (2048) larger than maxSize (1024)'\n  })\n\n  await t.completed\n})\n\ntest('Should dump response body up to limit (dispatch opts)', { skip }, async t => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const buffer = Buffer.alloc(1 * 1024)\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n    res.end(buffer)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    dumpMaxSize: 1 * 1024,\n    abortOnDumped: false\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump())\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n  const body = await response.body.text()\n\n  t.equal(response.statusCode, 200)\n  t.equal(response.headers['content-length'], `${1 * 1024}`)\n  t.equal(body, '')\n\n  await t.completed\n})\n\ntest('Should abort if content length grater than max size (dispatch opts)', { skip }, async t => {\n  t = tspl(t, { plan: 1 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    const buffer = Buffer.alloc(2 * 1024)\n    res.writeHead(200, {\n      'Content-Length': buffer.length,\n      'Content-Type': 'application/octet-stream'\n    })\n    res.end(buffer)\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    dumpMaxSize: 100\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(dump())\n\n  after(async () => {\n    await client.close()\n\n    server.close()\n    await once(server, 'close')\n  })\n\n  await t.rejects(\n    async () => {\n      return await client.request(requestOptions).then(res => res.body.text())\n    },\n    {\n      name: 'AbortError',\n      message: 'Response size (2048) larger than maxSize (100)'\n    }\n  )\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/interceptors/redirect-cross-origin-fix.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\nconst undici = require('../..')\n\nconst {\n  interceptors: { redirect }\n} = undici\n\ntest('Client should throw redirect loop error for cross-origin redirect', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const serverA = createServer((req, res) => {\n    res.writeHead(301, {\n      Location: 'http://localhost:9999/target' // Different port = cross-origin\n    })\n    res.end()\n  })\n\n  serverA.listen(0)\n  after(() => serverA.close())\n  await once(serverA, 'listening')\n\n  const client = new undici.Client(`http://localhost:${serverA.address().port}`).compose(\n    redirect({ maxRedirections: 2 })  // Keep low to avoid long waits\n  )\n  after(() => client.close())\n\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/test'\n    })\n    t.fail('Expected error but request succeeded')\n  } catch (error) {\n    t.ok(error.message.includes('Redirect loop detected'), 'Error message indicates redirect loop')\n    t.ok(error.message.includes('Client or Pool'), 'Error message mentions Client or Pool')\n  }\n\n  await t.completed\n})\n\ntest('Pool should throw redirect loop error for cross-origin redirect', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const serverA = createServer((req, res) => {\n    res.writeHead(301, {\n      Location: 'http://localhost:9998/target' // Different port = cross-origin\n    })\n    res.end()\n  })\n\n  serverA.listen(0)\n  after(() => serverA.close())\n  await once(serverA, 'listening')\n\n  const pool = new undici.Pool(`http://localhost:${serverA.address().port}`).compose(\n    redirect({ maxRedirections: 2 })  // Keep low to avoid long waits\n  )\n  after(() => pool.close())\n\n  try {\n    await pool.request({\n      method: 'GET',\n      path: '/test'\n    })\n    t.fail('Expected error but request succeeded')\n  } catch (error) {\n    t.ok(error.message.includes('Redirect loop detected'), 'Error message indicates redirect loop')\n    t.ok(error.message.includes('Client or Pool'), 'Error message mentions Client or Pool')\n  }\n\n  await t.completed\n})\n\ntest('Agent should successfully follow cross-origin redirect', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const serverB = createServer((req, res) => {\n    res.writeHead(200)\n    res.end('Cross-origin redirect success')\n  })\n\n  const serverA = createServer((req, res) => {\n    res.writeHead(301, {\n      Location: `http://localhost:${serverB.address().port}/success`\n    })\n    res.end()\n  })\n\n  serverA.listen(0)\n  serverB.listen(0)\n  after(() => {\n    serverA.close()\n    serverB.close()\n  })\n\n  await Promise.all([\n    once(serverA, 'listening'),\n    once(serverB, 'listening')\n  ])\n\n  const agent = new undici.Agent().compose(\n    redirect({ maxRedirections: 2 })\n  )\n  after(() => agent.close())\n\n  const response = await agent.request({\n    origin: `http://localhost:${serverA.address().port}`,\n    method: 'GET',\n    path: '/test'\n  })\n\n  const body = await response.body.text()\n\n  t.strictEqual(response.statusCode, 200, 'Response has 200 status code')\n  t.ok(body.includes('Cross-origin redirect success'), 'Response body indicates successful cross-origin redirect')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/interceptors/redirect-issue-3803.js",
    "content": "'use strict'\n\nconst { FormData, request, Agent, interceptors } = require('../..')\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\n\ntest('redirecting works with a FormData body', async (t) => {\n  const plan = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/1') {\n      res.writeHead(302, undefined, { location: '/2' })\n      res.end()\n    } else {\n      res.end('OK')\n    }\n  }).listen(0)\n\n  t.after(() => server.close())\n  await once(server, 'listening')\n\n  const agent = new Agent().compose(interceptors.redirect({ maxRedirections: 1 }))\n\n  const body = new FormData()\n  body.append('hello', 'world')\n\n  const { context } = await request(`http://localhost:${server.address().port}/1`, {\n    body,\n    method: 'POST',\n    dispatcher: agent\n  })\n\n  plan.deepStrictEqual(context.history, [\n    new URL(`http://localhost:${server.address().port}/1`),\n    new URL(`http://localhost:${server.address().port}/2`)\n  ])\n})\n"
  },
  {
    "path": "test/interceptors/redirect.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\nconst undici = require('../..')\nconst {\n  startRedirectingServer,\n  startRedirectingWithBodyServer,\n  startRedirectingChainServers,\n  startRedirectingWithoutLocationServer,\n  startRedirectingWithAuthorization,\n  startRedirectingWithCookie,\n  startRedirectingWithQueryParams,\n  startServer,\n  startRedirectingWithRelativePath\n} = require('../utils/redirecting-servers')\nconst { createReadable, createReadableStream } = require('../utils/stream')\n\nconst {\n  interceptors: { redirect }\n} = undici\n\nfor (const factory of [\n  (server, opts) =>\n    new undici.Agent(opts).compose(\n      redirect({ maxRedirections: opts?.maxRedirections })\n    ),\n  (server, opts) =>\n    new undici.Pool(`http://${server}`, opts).compose(\n      redirect({ maxRedirections: opts?.maxRedirections })\n    ),\n  (server, opts) =>\n    new undici.Client(`http://${server}`, opts).compose(\n      redirect({ maxRedirections: opts?.maxRedirections })\n    )\n]) {\n  const request = (t, server, opts, ...args) => {\n    const dispatcher = factory(server, opts)\n    after(() => dispatcher.close())\n    return undici.request(args[0], { ...args[1], dispatcher }, args[2])\n  }\n\n  test('should always have a history with the final URL even if no redirections were followed', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream,\n      context: { history }\n    } = await request(t, server, undefined, `http://${server}/200?key=value`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [`http://${server}/200?key=value`]\n    )\n    t.strictEqual(\n      body,\n      `GET /5 key=value :: host@${server} connection@keep-alive`\n    )\n\n    await t.completed\n  })\n\n  test('should not follow redirection by default if not using RedirectAgent', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}`)\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 302)\n    t.strictEqual(headers.location, `http://${server}/302/1`)\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 300', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream,\n      context: { history }\n    } = await request(t, server, undefined, `http://${server}/300?key=value`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [\n        `http://${server}/300?key=value`,\n        `http://${server}/300/1?key=value`,\n        `http://${server}/300/2?key=value`,\n        `http://${server}/300/3?key=value`,\n        `http://${server}/300/4?key=value`,\n        `http://${server}/300/5?key=value`\n      ]\n    )\n    t.strictEqual(\n      body,\n      `GET /5 key=value :: host@${server} connection@keep-alive`\n    )\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 300 default', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream,\n      context: { history }\n    } = await request(\n      t,\n      server,\n      { maxRedirections: 10 },\n      `http://${server}/300?key=value`\n    )\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [\n        `http://${server}/300?key=value`,\n        `http://${server}/300/1?key=value`,\n        `http://${server}/300/2?key=value`,\n        `http://${server}/300/3?key=value`,\n        `http://${server}/300/4?key=value`,\n        `http://${server}/300/5?key=value`\n      ]\n    )\n    t.strictEqual(\n      body,\n      `GET /5 key=value :: host@${server} connection@keep-alive`\n    )\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 301 changing method to GET', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'POST',\n      body: 'REQUEST',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(\n      body,\n      `GET /5 :: host@${server} connection@keep-alive`\n    )\n  })\n\n  test('should follow redirection after a HTTP 302', async t => {\n    t = tspl(t, { plan: 3 })\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/302`, {\n      method: 'PUT',\n      body: Buffer.from('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(\n      body,\n      `PUT /5 :: host@${server} connection@keep-alive content-length@7 :: REQUEST`\n    )\n  })\n\n  test('should follow redirection after a HTTP 303 changing method to GET', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      body: 'REQUEST',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (array)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: [\n        'Content-Encoding',\n        'gzip',\n        'X-Foo1',\n        '1',\n        'X-Foo2',\n        '2',\n        'Content-Type',\n        'application/json',\n        'X-Foo3',\n        '3',\n        'Host',\n        'localhost',\n        'X-Bar',\n        '4'\n      ],\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(\n      body,\n      `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`\n    )\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (object)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: {\n        'Content-Encoding': 'gzip',\n        'X-Foo1': '1',\n        'X-Foo2': '2',\n        'Content-Type': 'application/json',\n        'X-Foo3': '3',\n        Host: 'localhost',\n        'X-Bar': '4'\n      },\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(\n      body,\n      `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`\n    )\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 307', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/307`, {\n      method: 'DELETE',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `DELETE /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 308', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/308`, {\n      method: 'OPTIONS',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `OPTIONS /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should ignore HTTP 3xx response bodies', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingWithBodyServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream,\n      context: { history }\n    } = await request(t, server, undefined, `http://${server}/`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [`http://${server}/`, `http://${server}/end`]\n    )\n    t.strictEqual(body, 'FINAL')\n\n    await t.completed\n  })\n\n  test('should ignore query after redirection', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingWithQueryParams()\n\n    const {\n      statusCode,\n      headers,\n      context: { history }\n    } = await request(t, server, undefined, `http://${server}/`, {\n      maxRedirections: 10,\n      query: { param1: 'first' }\n    })\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [`http://${server}/`, `http://${server}/?param2=second`]\n    )\n\n    await t.completed\n  })\n\n  test('should follow a redirect chain up to the allowed number of times', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream,\n      context: { history }\n    } = await request(t, server, undefined, `http://${server}/300`, {\n      maxRedirections: 2\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 300)\n    t.strictEqual(headers.location, `http://${server}/300/3`)\n    t.deepStrictEqual(\n      history.map(x => x.toString()),\n      [\n        `http://${server}/300`,\n        `http://${server}/300/1`,\n        `http://${server}/300/2`\n      ]\n    )\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should follow a redirect chain up to the allowed number of times for redirectionLimitReached', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = await startRedirectingServer()\n\n    try {\n      await request(t, server, undefined, `http://${server}/300`, {\n        maxRedirections: 2,\n        throwOnMaxRedirect: true\n      })\n    } catch (error) {\n      if (error.message.startsWith('max redirects')) {\n        t.ok(true, 'Max redirects handled correctly')\n      } else {\n        t.fail(`Unexpected error: ${error.message}`)\n      }\n    }\n\n    await t.completed\n  })\n\n  test('when a Location response header is NOT present', async t => {\n    t = tspl(t, { plan: 6 * 3 })\n\n    const redirectCodes = [300, 301, 302, 303, 307, 308]\n    const server = await startRedirectingWithoutLocationServer()\n\n    for (const code of redirectCodes) {\n      const {\n        statusCode,\n        headers,\n        body: bodyStream\n      } = await request(t, server, undefined, `http://${server}/${code}`, {\n        maxRedirections: 10\n      })\n\n      const body = await bodyStream.text()\n\n      t.strictEqual(statusCode, code)\n      t.ok(!headers.location)\n      t.strictEqual(body.length, 0)\n    }\n    await t.completed\n  })\n\n  test('should not allow invalid maxRedirections arguments', async t => {\n    t = tspl(t, { plan: 1 })\n\n    try {\n      await request(t, 'localhost', undefined, 'http://localhost', {\n        method: 'GET',\n        maxRedirections: 'INVALID'\n      })\n\n      t.fail('Did not throw')\n    } catch (err) {\n      t.strictEqual(err.message, 'maxRedirections must be a positive number')\n    }\n    await t.completed\n  })\n\n  test('should not allow invalid maxRedirections arguments default', async t => {\n    t = tspl(t, { plan: 1 })\n\n    try {\n      await request(\n        t,\n        'localhost',\n        {\n          maxRedirections: 'INVALID'\n        },\n        'http://localhost',\n        {\n          method: 'GET'\n        }\n      )\n\n      t.fail('Did not throw')\n    } catch (err) {\n      t.strictEqual(err.message, 'maxRedirections must be a positive number')\n    }\n\n    await t.completed\n  })\n\n  test('should not follow redirects when using ReadableStream request bodies', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'PUT',\n      body: createReadableStream('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 301)\n    t.strictEqual(headers.location, `http://${server}/301/2`)\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should not follow redirects when using Readable request bodies', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      headers,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'PUT',\n      body: createReadable('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 301)\n    t.strictEqual(headers.location, `http://${server}/301/1`)\n    t.strictEqual(body.length, 0)\n    await t.completed\n  })\n\n  test('should follow redirects when using Readable request bodies w/ POST 101', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = await startRedirectingServer()\n\n    const {\n      statusCode,\n      body: bodyStream\n    } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'POST',\n      body: createReadable('REQUEST'),\n      maxRedirections: 10\n    })\n\n    await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    await t.completed\n  })\n}\n\ntest('should follow redirections when going cross origin', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const [server1, server2, server3] = await startRedirectingChainServers()\n\n  const {\n    statusCode,\n    headers,\n    body: bodyStream,\n    context: { history }\n  } = await undici.request(`http://${server1}`, {\n    method: 'POST',\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 }))\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(statusCode, 200)\n  t.ok(!headers.location)\n  t.deepStrictEqual(\n    history.map(x => x.toString()),\n    [\n      `http://${server1}/`,\n      `http://${server2}/`,\n      `http://${server3}/`,\n      `http://${server2}/end`,\n      `http://${server3}/end`,\n      `http://${server1}/end`\n    ]\n  )\n  t.strictEqual(body, 'GET')\n\n  await t.completed\n})\n\ntest('should handle errors (callback)', async t => {\n  t = tspl(t, { plan: 1 })\n\n  undici.request(\n    'http://localhost:0',\n    {\n      dispatcher: new undici.Agent({}).compose(\n        redirect({ maxRedirections: 10 })\n      )\n    },\n    error => {\n      t.match(error.code, /EADDRNOTAVAIL|ECONNREFUSED/)\n    }\n  )\n\n  await t.completed\n})\n\ntest('should handle errors (promise)', async t => {\n  t = tspl(t, { plan: 1 })\n\n  try {\n    await undici.request('http://localhost:0', {\n      dispatcher: new undici.Agent({}).compose(\n        redirect({ maxRedirections: 10 })\n      )\n    })\n    t.fail('Did not throw')\n  } catch (error) {\n    t.match(error.code, /EADDRNOTAVAIL|ECONNREFUSED/)\n  }\n\n  await t.completed\n})\n\ntest('removes authorization header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const [server1] = await startRedirectingWithAuthorization('secret')\n  const { body: bodyStream } = await undici.request(`http://${server1}`, {\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 })),\n    headers: {\n      authorization: 'secret'\n    }\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(body, '')\n\n  await t.completed\n})\n\ntest('removes cookie header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n  const [server1] = await startRedirectingWithCookie('a=b')\n  const { body: bodyStream } = await undici.request(`http://${server1}`, {\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 })),\n    headers: {\n      cookie: 'a=b'\n    }\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(body, '')\n\n  await t.completed\n})\n\ntest('should upgrade the connection when no redirects are present', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = await startServer((req, res) => {\n    if (req.url === '/') {\n      res.statusCode = 301\n      res.setHeader('Location', `http://${server}/end`)\n      res.end('REDIRECT')\n      return\n    }\n\n    res.statusCode = 101\n    res.setHeader('Connection', 'upgrade')\n    res.setHeader('Upgrade', req.headers.upgrade)\n    res.end('')\n  })\n\n  const { headers, socket } = await undici.upgrade(`http://${server}/`, {\n    method: 'GET',\n    protocol: 'foo/1',\n    dispatcher: new undici.Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 }))\n  })\n\n  socket.end()\n\n  t.strictEqual(headers.connection, 'upgrade')\n  t.strictEqual(headers.upgrade, 'foo/1')\n\n  await t.completed\n})\n\ntest('should redirect to relative URL according to RFC 7231', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = await startRedirectingWithRelativePath()\n\n  const { statusCode, body } = await undici.request(`http://${server}`, {\n    dispatcher: new undici.Client(`http://${server}/`).compose(redirect({ maxRedirections: 3 }))\n  })\n\n  const finalPath = await body.text()\n\n  t.strictEqual(statusCode, 200)\n  t.strictEqual(finalPath, '/absolute/b')\n})\n\ntest('same-origin redirect preserves plain object headers with polluted Object.prototype[Symbol.iterator]', async (t) => {\n  const { strictEqual } = tspl(t, { plan: 2 })\n\n  const server = createServer((req, res) => {\n    if (req.url === '/redirect') {\n      res.writeHead(302, {\n        Location: '/final'\n      })\n      res.end()\n      return\n    }\n\n    strictEqual(req.headers['x-custom'], 'ok')\n    res.end('redirected')\n  }).listen(0)\n\n  const originalIterator = Object.prototype[Symbol.iterator]\n  // eslint-disable-next-line no-extend-native\n  Object.prototype[Symbol.iterator] = function * () {}\n\n  try {\n    await once(server, 'listening')\n\n    const res = await undici.request(`http://localhost:${server.address().port}/redirect`, {\n      dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 1 })),\n      headers: {\n        'X-Custom': 'ok'\n      }\n    })\n\n    const text = await res.body.text()\n    strictEqual(text, 'redirected')\n  } finally {\n    if (originalIterator === undefined) {\n      delete Object.prototype[Symbol.iterator]\n    } else {\n      // eslint-disable-next-line no-extend-native\n      Object.prototype[Symbol.iterator] = originalIterator\n    }\n    server.close()\n  }\n})\n\ntest('Cross-origin redirects clear forbidden headers', async (t) => {\n  const { strictEqual } = tspl(t, { plan: 6 })\n\n  const server1 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    strictEqual(req.headers.cookie, undefined)\n    strictEqual(req.headers.authorization, undefined)\n    strictEqual(req.headers['proxy-authorization'], undefined)\n\n    res.end('redirected')\n  }).listen(0)\n\n  const server2 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    strictEqual(req.headers.authorization, 'test')\n    strictEqual(req.headers.cookie, 'ddd=dddd')\n\n    res.writeHead(302, {\n      ...req.headers,\n      Location: `http://localhost:${server1.address().port}`\n    })\n    res.end()\n  }).listen(0)\n\n  t.after(() => {\n    server1.close()\n    server2.close()\n  })\n\n  await Promise.all([\n    once(server1, 'listening'),\n    once(server2, 'listening')\n  ])\n\n  const res = await undici.request(`http://localhost:${server2.address().port}`, {\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 1 })),\n    headers: {\n      Authorization: 'test',\n      Cookie: 'ddd=dddd',\n      'Proxy-Authorization': 'test'\n    }\n  })\n\n  const text = await res.body.text()\n  strictEqual(text, 'redirected')\n})\n"
  },
  {
    "path": "test/interceptors/response-error.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { test, after } = require('node:test')\nconst { interceptors, Client } = require('../..')\nconst { responseError } = interceptors\n\ntest('should throw error for error response', async () => {\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.writeHead(400, { 'content-type': 'text/plain' })\n    res.end('Bad Request')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  let error\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'text/plain'\n      }\n    })\n  } catch (err) {\n    error = err\n  }\n\n  assert.equal(error.statusCode, 400)\n  assert.equal(error.message, 'Response Error')\n  assert.equal(error.body, 'Bad Request')\n})\n\ntest('should not throw error for ok response', async () => {\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('hello')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'text/plain'\n    }\n  })\n\n  assert.equal(response.statusCode, 200)\n  assert.equal(await response.body.text(), 'hello')\n})\n\ntest('should throw error for error response, parsing JSON', async () => {\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.writeHead(400, { 'content-type': 'application/json; charset=utf-8' })\n    res.end(JSON.stringify({ message: 'Bad Request' }))\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  let error\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'text/plain'\n      }\n    })\n  } catch (err) {\n    error = err\n  }\n\n  assert.equal(error.statusCode, 400)\n  assert.equal(error.message, 'Response Error')\n  assert.deepStrictEqual(error.body, {\n    message: 'Bad Request'\n  })\n})\n\ntest('should throw error for error response, parsing JSON without charset', async () => {\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.writeHead(400, { 'content-type': 'application/json' })\n    res.end(JSON.stringify({ message: 'Bad Request' }))\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  let error\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'text/plain'\n      }\n    })\n  } catch (err) {\n    error = err\n  }\n\n  assert.equal(error.statusCode, 400)\n  assert.equal(error.message, 'Response Error')\n  assert.deepStrictEqual(error.body, {\n    message: 'Bad Request'\n  })\n})\n\ntest('should throw error for networking errors response', async () => {\n  const client = new Client(\n    'http://localhost:12345'\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n  })\n\n  let error\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'text/plain'\n      }\n    })\n  } catch (err) {\n    error = err\n  }\n\n  assert.equal(error.code, 'ECONNREFUSED')\n})\n\ntest('should throw error for error response without content type', async () => {\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.on('request', (req, res) => {\n    res.writeHead(400, {})\n    res.end()\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(responseError())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  let error\n  try {\n    await client.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'text/plain'\n      }\n    })\n  } catch (err) {\n    error = err\n  }\n\n  assert.equal(error.statusCode, 400)\n  assert.equal(error.message, 'Response Error')\n  assert.deepStrictEqual(error.body, '')\n})\n"
  },
  {
    "path": "test/interceptors/retry.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { spawnSync } = require('node:child_process')\n\nconst { Client, interceptors } = require('../..')\nconst { retry, redirect, dns } = interceptors\n\ntest('Should retry status code', async t => {\n  t = tspl(t, { plan: 4 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const retryOptions = {\n    retry: (err, { state, opts }, done) => {\n      counter++\n\n      if (err.statusCode === 500 || err.message.includes('other side closed')) {\n        setTimeout(done, 500)\n        return\n      }\n\n      return done(err)\n    }\n  }\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        t.ok(true, 'pass')\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        t.ok(true, 'pass')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry(retryOptions))\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('Should retry on error code', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const retryOptions = {\n    retry: (err, _state, done) => {\n      if (counter < 5) {\n        counter++\n        setTimeout(done, 500)\n      } else {\n        done(err)\n      }\n    },\n    maxRetries: 5\n  }\n  const requestOptions = {\n    origin: 'http://localhost:123',\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  const client = new Client(\n    'http://localhost:123'\n  ).compose(dns({\n    lookup: (_h, _o, cb) => {\n      const error = new Error('ENOTFOUND')\n      error.code = 'ENOTFOUND'\n\n      cb(error)\n    }\n  }), retry(retryOptions))\n\n  after(async () => {\n    await client.close()\n  })\n\n  await t.rejects(client.request(requestOptions), { code: 'ENOTFOUND' })\n  t.equal(counter, 5)\n})\n\ntest('Should use retry-after header for retries', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(429, {\n          'retry-after': 1\n        })\n        res.end('rate limit')\n        checkpoint = Date.now()\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 500)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(dispatchOptions)\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('Should use retry-after header for retries (date)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const requestOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        checkpoint = Date.now()\n        res.writeHead(429, {\n          'retry-after': new Date(\n            checkpoint + 2000\n          ).toUTCString()\n        })\n        res.end('rate limit')\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 1000)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('Should retry with defaults', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        counter++\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        counter++\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        counter++\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('Should pass context from other interceptors', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'GET',\n    path: '/'\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(200)\n    res.end('hello world!')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(redirect({ maxRedirections: 1 }), retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  t.equal(response.statusCode, 200)\n  t.deepStrictEqual(response.context, { history: [] })\n})\n\ntest('Should handle 206 partial content', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('content-length', '6')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-5')\n      res.setHeader('content-range', 'bytes 3-5/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const retryOptions = {\n    retry: function (err, _, done) {\n      counter++\n\n      if (err.code && err.code === 'UND_ERR_DESTROYED') {\n        return done(false)\n      }\n\n      if (err.statusCode === 206) return done(err)\n\n      setTimeout(done, 800)\n    }\n  }\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    retryOptions\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n\n  t.equal(response.statusCode, 200)\n  t.strictEqual(await response.body.text(), 'abcdef')\n  t.strictEqual(counter, 1)\n})\n\ntest('Should handle 206 partial content - bad-etag', async t => {\n  t = tspl(t, { plan: 5 })\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'erwsd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const requestOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        if (err.message.includes('other side closed')) {\n          setTimeout(done, 100)\n          return\n        }\n\n        return done(err)\n      }\n    }\n  }\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  try {\n    const response = await client.request(requestOptions)\n    await response.body.text()\n  } catch (error) {\n    t.strictEqual(error.name, 'RequestRetryError')\n    t.strictEqual(error.code, 'UND_ERR_REQ_RETRY')\n    t.strictEqual(error.message, 'ETag mismatch')\n  }\n})\n\ntest('retrying a request with a body', async t => {\n  t = tspl(t, { plan: 2 })\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestOptions = {\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: JSON.stringify({ hello: 'world' }),\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request(requestOptions)\n  t.equal(response.statusCode, 200)\n  t.equal(await response.body.text(), 'hello world!')\n})\n\ntest('should not error if request is not meant to be retried', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.writeHead(400)\n    res.end('Bad request')\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(\n    `http://localhost:${server.address().port}`\n  ).compose(retry())\n\n  after(async () => {\n    await client.close()\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  })\n\n  t.equal(response.statusCode, 400)\n  t.equal(await response.body.text(), 'Bad request')\n})\n\ntest('#3975 - keep event loop ticking', async t => {\n  const suite = tspl(t, { plan: 2 })\n\n  const res = spawnSync('node', ['./test/fixtures/interceptors/retry-event-loop.js'], {\n    stdio: 'pipe'\n  })\n\n  const output = res.stderr.toString()\n  suite.ok(output.includes('UND_ERR_REQ_RETRY'))\n  suite.ok(output.includes('RequestRetryError: Request failed'))\n})\n"
  },
  {
    "path": "test/invalid-headers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\n\ntest('invalid headers', (t) => {\n  t = tspl(t, { plan: 10 })\n\n  const client = new Client('http://localhost:3000')\n  after(() => client.close())\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'content-length': 'asd'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: 1\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'transfer-encoding': 'chunked'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      upgrade: 'asd'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      connection: 'asd'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'keep-alive': 'timeout=5'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      foo: {}\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      expect: '100-continue'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.NotSupportedError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      Expect: '100-continue'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.NotSupportedError)\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      expect: 'asd'\n    }\n  }, (err, data) => {\n    t.ok(err instanceof errors.NotSupportedError)\n  })\n})\n"
  },
  {
    "path": "test/ip-prioritization.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\ntest('HTTP/1.1 Request Prioritization', async (t) => {\n  let priority = null\n\n  const server = createServer((req, res) => {\n    res.end('ok')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    connect: (opts, cb) => {\n      const socket = require('node:net').connect({\n        ...opts,\n        host: opts.hostname,\n        port: opts.port\n      }, () => {\n        cb(null, socket)\n      })\n      socket.setTypeOfService = (p) => {\n        priority = p\n      }\n      return socket\n    }\n  })\n\n  try {\n    await client.request({\n      path: '/',\n      method: 'GET',\n      typeOfService: 42\n    })\n\n    // Check if priority was set\n    if (priority !== 42) {\n      throw new Error(`Expected priority 42, got ${priority}`)\n    }\n  } finally {\n    await client.close()\n    server.close()\n  }\n})\n\ntest('HTTP/2 Connection Prioritization', async (t) => {\n  const net = require('node:net')\n  const buildConnector = require('../lib/core/connect')\n\n  let receivedHints = null\n  // Mock net.connect\n  t.mock.method(net, 'connect', (options) => {\n    receivedHints = options.typeOfService\n\n    const socket = new (require('node:events').EventEmitter)()\n    socket.cork = () => { }\n    socket.uncork = () => { }\n    socket.destroy = () => { }\n    socket.ref = () => { }\n    socket.unref = () => { }\n    socket.setKeepAlive = () => socket\n    socket.setNoDelay = () => socket\n\n    // Simulate connection to allow callback to fire\n    process.nextTick(() => {\n      socket.emit('connect')\n    })\n\n    return socket\n  })\n\n  // Test buildConnector directly to ensure options passing\n  const connector = buildConnector({ typeOfService: 123, allowH2: true })\n\n  await new Promise((resolve, reject) => {\n    connector({ hostname: 'localhost', host: 'localhost', protocol: 'http:', port: 3000 }, (err, socket) => {\n      if (err) reject(err)\n      else resolve(socket)\n    })\n  })\n\n  if (receivedHints !== 123) {\n    throw new Error(`Expected typeOfService 123, got ${receivedHints}`)\n  }\n})\n"
  },
  {
    "path": "test/issue-1757.js",
    "content": "'use strict'\n\nconst { deepStrictEqual, strictEqual } = require('node:assert')\nconst { test } = require('node:test')\nconst { Dispatcher, setGlobalDispatcher, fetch, MockAgent } = require('..')\n\nclass MiniflareDispatcher extends Dispatcher {\n  constructor (inner, options) {\n    super(options)\n    this.inner = inner\n  }\n\n  dispatch (options, handler) {\n    return this.inner.dispatch(options, handler)\n  }\n\n  close (...args) {\n    return this.inner.close(...args)\n  }\n\n  destroy (...args) {\n    return this.inner.destroy(...args)\n  }\n}\n\ntest('https://github.com/nodejs/undici/issues/1757', async () => {\n  const mockAgent = new MockAgent()\n  const mockClient = mockAgent.get('http://localhost:3000')\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(new MiniflareDispatcher(mockAgent))\n\n  mockClient.intercept({\n    path: () => true,\n    method: () => true\n  }).reply(200, async (opts) => {\n    if (opts.body?.[Symbol.asyncIterator]) {\n      const chunks = []\n      for await (const chunk of opts.body) {\n        chunks.push(chunk)\n      }\n\n      return Buffer.concat(chunks)\n    }\n\n    return opts.body\n  })\n\n  const response = await fetch('http://localhost:3000', {\n    method: 'POST',\n    body: JSON.stringify({ foo: 'bar' })\n  })\n\n  deepStrictEqual(await response.json(), { foo: 'bar' })\n  strictEqual(response.status, 200)\n})\n"
  },
  {
    "path": "test/issue-2065.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { FormData, request } = require('..')\n\ntest('undici.request with a FormData body should set content-length header', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.ok(req.headers['content-length'])\n    res.end()\n  }).listen(0)\n\n  after(() => server.close())\n  await once(server, 'listening')\n\n  const body = new FormData()\n  body.set('file', new File(['abc'], 'abc.txt'))\n\n  await request(`http://localhost:${server.address().port}`, {\n    method: 'POST',\n    body\n  })\n})\n"
  },
  {
    "path": "test/issue-2078.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { MockAgent, getGlobalDispatcher, setGlobalDispatcher, fetch } = require('..')\n\ntest('MockPool.reply headers are an object, not an array - issue #2078', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const global = getGlobalDispatcher()\n  const mockAgent = new MockAgent()\n  const mockPool = mockAgent.get('http://localhost')\n\n  after(() => setGlobalDispatcher(global))\n  setGlobalDispatcher(mockAgent)\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply((options) => {\n    t.strictEqual(Array.isArray(options.headers), false)\n\n    return { statusCode: 200 }\n  })\n\n  await fetch('http://localhost/foo')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-2283.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst { FormData, Response } = require('..')\n\ndescribe('https://github.com/nodejs/undici/issues/2283', () => {\n  test('preserve full type when parsing multipart/form-data', async (t) => {\n    const testBlob = new Blob(['123'], { type: 'text/plain;charset=utf-8' })\n    const fd = new FormData()\n    fd.set('x', testBlob)\n    const res = new Response(fd)\n\n    const body = await res.clone().text()\n\n    // Just making sure that it contains ;charset=utf-8\n    assert.ok(body.includes('text/plain;charset=utf-8'))\n\n    const formData = await new Response(fd).formData()\n\n    // returns just 'text/plain'\n    assert.ok(formData.get('x').type === 'text/plain;charset=utf-8')\n  })\n})\n"
  },
  {
    "path": "test/issue-2349.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { rejects } = require('node:assert')\nconst { Writable } = require('node:stream')\nconst { MockAgent, stream } = require('..')\n\ntest('stream() does not fail after request has been aborted', () => {\n  const mockAgent = new MockAgent()\n\n  mockAgent.disableNetConnect()\n  mockAgent\n    .get('http://localhost:3333')\n    .intercept({\n      path: '/'\n    })\n    .reply(200, 'ok')\n    .delay(10)\n\n  const parts = []\n  const ac = new AbortController()\n\n  setTimeout(() => ac.abort(), 5)\n\n  rejects(\n    stream(\n      'http://localhost:3333/',\n      {\n        opaque: { parts },\n        signal: ac.signal,\n        dispatcher: mockAgent\n      },\n      ({ opaque: { parts } }) => {\n        return new Writable({\n          write (chunk, _encoding, callback) {\n            parts.push(chunk)\n            callback()\n          }\n        })\n      }\n    ),\n    new DOMException('This operation was aborted', 'AbortError')\n  )\n})\n"
  },
  {
    "path": "test/issue-2590.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { request } = require('..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\ntest('aborting request with custom reason', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, () => {}).listen(0)\n\n  after(() => server.close())\n  await once(server, 'listening')\n\n  const timeout = AbortSignal.timeout(0)\n  const ac = new AbortController()\n  ac.abort(new Error('aborted'))\n\n  const ac2 = new AbortController()\n  ac2.abort() // no reason\n\n  await t.rejects(\n    request(`http://localhost:${server.address().port}`, { signal: timeout }),\n    timeout.reason\n  )\n\n  await t.rejects(\n    request(`http://localhost:${server.address().port}`, { signal: ac.signal }),\n    /Error: aborted/\n  )\n\n  await t.rejects(\n    request(`http://localhost:${server.address().port}`, { signal: ac2.signal }),\n    { name: 'AbortError' }\n  )\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-3356.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { setTimeout: sleep } = require('node:timers/promises')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tick: fastTimersTick } = require('../lib/util/timers')\nconst { fetch, Agent, RetryAgent } = require('..')\n\ntest('https://github.com/nodejs/undici/issues/3356', { skip: process.env.CITGM }, async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  let shouldRetry = true\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    if (shouldRetry) {\n      shouldRetry = false\n\n      res.flushHeaders()\n      res.write('h')\n      setTimeout(() => { res.end('ello world!') }, 100)\n    } else {\n      res.end('hello world!')\n    }\n  })\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  after(async () => {\n    server.close()\n\n    await once(server, 'close')\n  })\n\n  const agent = new RetryAgent(new Agent({ bodyTimeout: 50 }), {\n    errorCodes: ['UND_ERR_BODY_TIMEOUT']\n  })\n\n  const response = await fetch(`http://localhost:${server.address().port}`, {\n    dispatcher: agent\n  })\n\n  fastTimersTick()\n\n  await sleep(500)\n\n  try {\n    t.equal(response.status, 200)\n    // consume response\n    await response.text()\n  } catch (err) {\n    t.equal(err.name, 'TypeError')\n    t.equal(err.cause.code, 'UND_ERR_REQ_RETRY')\n  }\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-3410.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { fork } = require('node:child_process')\nconst { resolve: pathResolve } = require('node:path')\nconst { describe, test } = require('node:test')\nconst { Agent, fetch, setGlobalDispatcher } = require('..')\nconst { eventLoopBlocker } = require('./utils/event-loop-blocker')\n\ndescribe('https://github.com/nodejs/undici/issues/3410', () => {\n  test('FastTimers', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    // Spawn a server in a new process to avoid effects from the blocking event loop\n    const {\n      serverProcess,\n      address\n    } = await new Promise((resolve, reject) => {\n      const childProcess = fork(\n        pathResolve(__dirname, './utils/hello-world-server.js'),\n        [],\n        { windowsHide: true }\n      )\n\n      childProcess.on('message', (address) => {\n        resolve({\n          serverProcess: childProcess,\n          address\n        })\n      })\n      childProcess.on('error', err => {\n        reject(err)\n      })\n    })\n\n    const connectTimeout = 2000\n    setGlobalDispatcher(new Agent({ connectTimeout }))\n\n    const fetchPromise = fetch(address)\n\n    eventLoopBlocker(3000)\n\n    const response = await fetchPromise\n\n    t.equal(await response.text(), 'Hello World')\n\n    serverProcess.kill('SIGKILL')\n  })\n\n  test('native Timers', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    // Spawn a server in a new process to avoid effects from the blocking event loop\n    const {\n      serverProcess,\n      address\n    } = await new Promise((resolve, reject) => {\n      const childProcess = fork(\n        pathResolve(__dirname, './utils/hello-world-server.js'),\n        [],\n        { windowsHide: true }\n      )\n\n      childProcess.on('message', (address) => {\n        resolve({\n          serverProcess: childProcess,\n          address\n        })\n      })\n      childProcess.on('error', err => {\n        reject(err)\n      })\n    })\n\n    const connectTimeout = 900\n    setGlobalDispatcher(new Agent({ connectTimeout }))\n\n    const fetchPromise = fetch(address)\n\n    eventLoopBlocker(1500)\n\n    const response = await fetchPromise\n\n    t.equal(await response.text(), 'Hello World')\n\n    serverProcess.kill('SIGKILL')\n  })\n})\n"
  },
  {
    "path": "test/issue-3904.js",
    "content": "const { describe, test, after } = require('node:test')\nconst assert = require('node:assert')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst MemoryCacheStore = require('../lib/cache/memory-cache-store.js')\nconst { Agent, interceptors, request, setGlobalDispatcher } = require('..')\n\ndescribe('Cache with cache-control: no-store request header', () => {\n  [\n    'CACHE-CONTROL',\n    'cache-control',\n    'Cache-Control'\n  ].forEach(headerName => {\n    test(`should not cache response for request with header: \"${headerName}: no-store`, async () => {\n      const store = new MemoryCacheStore()\n      let requestCount = 0\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        ++requestCount\n        res.setHeader('Vary', 'Accept-Encoding')\n        res.setHeader('Cache-Control', 'max-age=60')\n        res.end(`Request count: ${requestCount}`)\n      })\n\n      after(async () => {\n        server.close()\n\n        await once(server, 'close')\n      })\n\n      await new Promise(resolve => server.listen(0, resolve))\n      const { port } = server.address()\n      const url = `http://localhost:${port}`\n\n      const agent = new Agent()\n      setGlobalDispatcher(\n        agent.compose(\n          interceptors.cache({\n            store,\n            cacheByDefault: 1000,\n            methods: ['GET']\n          })\n        )\n      )\n\n      const res1 = await request(url, { headers: { [headerName]: 'no-store' } })\n      const body1 = await res1.body.text()\n      assert.strictEqual(body1, 'Request count: 1')\n      assert.strictEqual(requestCount, 1)\n\n      const res2 = await request(url)\n      const body2 = await res2.body.text()\n      assert.strictEqual(body2, 'Request count: 2')\n      assert.strictEqual(requestCount, 2)\n\n      await new Promise(resolve => server.close(resolve))\n    })\n  })\n})\n"
  },
  {
    "path": "test/issue-3934.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst assert = require('node:assert')\nconst { Agent, RetryAgent, request } = require('..')\n\n// https://github.com/nodejs/undici/issues/3934\ntest('WrapHandler works with multiple header values', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true }, async (_req, res) => {\n    const headers = [\n      ['set-cookie', 'a'],\n      ['set-cookie', 'b'],\n      ['set-cookie', 'c']\n    ]\n    res.writeHead(200, headers)\n    res.end()\n  }).listen(0)\n\n  await once(server, 'listening')\n  t.after(() => server.close())\n\n  const agent = new Agent()\n  const retryAgent = new RetryAgent(agent)\n\n  const {\n    headers\n  } = await request(`http://localhost:${server.address().port}`, { dispatcher: retryAgent })\n\n  assert.deepStrictEqual(headers['set-cookie'], ['a', 'b', 'c'])\n})\n\n// https://github.com/nodejs/undici/issues/4797\ntest('WrapHandler preserves latin1 header bytes', async (t) => {\n  const expected = Buffer.from([0xE2, 0x80, 0xA6]).toString('latin1')\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (_req, res) => {\n    res.writeHead(200, {\n      'x-test': expected\n    })\n    res.end()\n  }).listen(0)\n\n  await once(server, 'listening')\n  t.after(() => server.close())\n\n  const agent = new Agent()\n  const retryAgent = new RetryAgent(agent)\n\n  const {\n    headers\n  } = await request(`http://localhost:${server.address().port}`, { dispatcher: retryAgent })\n\n  assert.strictEqual(headers['x-test'], expected)\n})\n"
  },
  {
    "path": "test/issue-3959.js",
    "content": "const { describe, test, after } = require('node:test')\nconst assert = require('node:assert')\nconst { createServer } = require('node:http')\nconst MemoryCacheStore = require('../lib/cache/memory-cache-store.js')\nconst { request, Agent, setGlobalDispatcher } = require('..')\nconst { interceptors } = require('..')\nconst { runtimeFeatures } = require('../lib/util/runtime-features.js')\n\ndescribe('Cache with Vary headers', () => {\n  async function runCacheTest (store) {\n    let requestCount = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      requestCount++\n      res.setHeader('Vary', 'Accept-Encoding')\n      res.setHeader('Cache-Control', 'max-age=60')\n      res.end(`Request count: ${requestCount}`)\n    })\n\n    await new Promise(resolve => server.listen(0, resolve))\n    const port = server.address().port\n    const url = `http://localhost:${port}`\n\n    const agent = new Agent()\n    setGlobalDispatcher(\n      agent.compose(\n        interceptors.cache({\n          store,\n          cacheByDefault: 1000,\n          methods: ['GET']\n        })\n      )\n    )\n\n    const res1 = await request(url)\n    const body1 = await res1.body.text()\n    assert.strictEqual(body1, 'Request count: 1')\n    assert.strictEqual(requestCount, 1)\n\n    const res2 = await request(url)\n    const body2 = await res2.body.text()\n    assert.strictEqual(body2, 'Request count: 1')\n    assert.strictEqual(requestCount, 1)\n\n    const res3 = await request(url, {\n      headers: {\n        'Accept-Encoding': 'gzip'\n      }\n    })\n    const body3 = await res3.body.text()\n    assert.strictEqual(body3, 'Request count: 2')\n    assert.strictEqual(requestCount, 2)\n\n    await new Promise(resolve => server.close(resolve))\n  }\n\n  test('should cache response with MemoryCacheStore when Vary header exists but request header is missing', async () => {\n    await runCacheTest(new MemoryCacheStore())\n  })\n\n  test('should cache response with SqliteCacheStore when Vary header exists but request header is missing', { skip: runtimeFeatures.has('sqlite') === false }, async () => {\n    const SqliteCacheStore = require('../lib/cache/sqlite-cache-store.js')\n    const sqliteStore = new SqliteCacheStore()\n    await runCacheTest(sqliteStore)\n    after(() => sqliteStore.close())\n  })\n})\n"
  },
  {
    "path": "test/issue-4244.js",
    "content": "const { test, describe } = require('node:test')\nconst assert = require('node:assert')\nconst { createServer } = require('node:http')\nconst { request, Agent, Pool } = require('..')\n\n// https://github.com/nodejs/undici/issues/4424\ndescribe('Agent should close inactive clients', () => {\n  test('without active connections', async (t) => {\n    const server = createServer({ keepAliveTimeout: 0 }, async (_req, res) => {\n      res.setHeader('connection', 'close')\n      res.writeHead(200)\n      res.end('ok')\n    }).listen(0)\n\n    t.after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    let p\n    const agent = new Agent({\n      factory: (origin, opts) => {\n        const pool = new Pool(origin, opts)\n        let _resolve, _reject\n        p = new Promise((resolve, reject) => {\n          _resolve = resolve\n          _reject = reject\n        })\n        pool.on('disconnect', () => {\n          setImmediate(() => pool.destroyed ? _resolve() : _reject(new Error('client not destroyed')))\n        })\n        return pool\n      }\n    })\n    const { statusCode } = await request(`http://localhost:${server.address().port}`, { dispatcher: agent })\n    assert.equal(statusCode, 200)\n\n    await p\n  })\n\n  test('in case of connection error', async (t) => {\n    let p\n    const agent = new Agent({\n      factory: (origin, opts) => {\n        const pool = new Pool(origin, opts)\n        let _resolve, _reject\n        p = new Promise((resolve, reject) => {\n          _resolve = resolve\n          _reject = reject\n        })\n        pool.on('connectionError', () => {\n          setImmediate(() => pool.destroyed ? _resolve() : _reject(new Error('client not destroyed')))\n        })\n        return pool\n      }\n    })\n    try {\n      await request('http://localhost:0', { dispatcher: agent })\n    } catch (_) {\n      // ignore\n    }\n\n    await p\n  })\n})\n"
  },
  {
    "path": "test/issue-4691.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\nconst { RetryAgent, Client, FormData } = require('..')\n// https://github.com/nodejs/undici/issues/4691\ntest('Should retry status code with FormData body', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const opts = {\n    maxRetries: 5,\n    timeout: 1,\n    timeoutFactor: 1\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter++) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const agent = new RetryAgent(client, opts)\n\n    after(async () => {\n      await agent.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    const formData = new FormData()\n    formData.append('field', 'test string')\n    agent.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      },\n      body: formData\n    }).then((res) => {\n      t.equal(res.statusCode, 200)\n      res.body.setEncoding('utf8')\n      let chunks = ''\n      res.body.on('data', chunk => { chunks += chunk })\n      res.body.on('end', () => {\n        t.equal(chunks, 'hello world!')\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-4806.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst { Agent, request } = require('..')\n\n// https://github.com/nodejs/undici/issues/4806\ntest('Agent clientTtl cleanup does not trigger unhandled rejections', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer((req, res) => {\n    res.end('ok')\n  })\n\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const agent = new Agent({ clientTtl: 10 })\n    after(async () => agent.close())\n\n    const onUnhandled = (err) => t.fail(err)\n    process.once('unhandledRejection', onUnhandled)\n    after(() => process.removeListener('unhandledRejection', onUnhandled))\n\n    const origin = `http://localhost:${server.address().port}`\n\n    const res1 = await request(origin, { dispatcher: agent })\n    t.strictEqual(res1.statusCode, 200)\n\n    await new Promise(resolve => setTimeout(resolve, 20))\n\n    const res2 = await request(origin, { dispatcher: agent })\n    t.strictEqual(res2.statusCode, 200)\n    res2.body.resume()\n    await once(res2.body, 'end')\n\n    await new Promise(resolve => setTimeout(resolve, 20))\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-4880.js",
    "content": "'use strict'\n\n// Regression test for: \"Cannot read properties of null (reading 'push')\"\n//\n// Stack trace:\n//   TypeError: Cannot read properties of null (reading 'push')\n//   at RequestHandler.onData (lib/api/api-request.js:148:21)\n//   at ClientHttp2Stream.<anonymous> (lib/dispatcher/client-h2.js:706:17)\n//\n// Root cause:\n//   The 'data' listener was registered unconditionally on the stream, outside\n//   the 'response' handler. This meant 'data' events could fire before 'response',\n//   i.e. before onHeaders() ran and set RequestHandler.res. With res still null\n//   (its initial value), onData() -> this.res.push(chunk) crashed.\n//\n//   Fix: register the 'data' listener only inside the 'response' handler, after\n//   onHeaders() has run and res is guaranteed to be set. A closure-local\n//   dataHandlerActive flag additionally guards against already-queued 'data'\n//   events that arrive after abort() tears down the stream.\n//\n// Reproduced by: TLS H2 server with slow responses (2.5s), 20 connections x 100\n// concurrent streams = 2000 max concurrent, 10k total requests queued. Under this\n// backpressure undici dispatches data events before response headers are processed.\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Worker, isMainThread, parentPort } = require('node:worker_threads')\nconst { Agent, interceptors } = require('..')\n\n// ── Server (runs in worker thread) ──────────────────────────────────────────\nif (!isMainThread) {\n  const http2 = require('node:http2')\n  const pem = require('@metcoder95/https-pem')\n\n  pem.generate({ opts: { keySize: 2048 } }).then((cert) => {\n    const body = JSON.stringify({ ok: true })\n    const server = http2.createSecureServer({ key: cert.key, cert: cert.cert, allowHTTP1: false })\n\n    server.on('stream', (stream, headers) => {\n      const path = headers[':path'] ?? ''\n      if (path.startsWith('/slow')) {\n        const url = new URL(path, 'https://localhost')\n        const delayMs = parseInt(url.searchParams.get('delayMs') ?? '2500')\n        setTimeout(() => {\n          stream.respond({ ':status': 200, 'content-type': 'application/json' })\n          stream.end(body)\n        }, delayMs)\n      } else {\n        stream.respond({ ':status': 200, 'content-type': 'application/json' })\n        stream.end(body)\n      }\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      parentPort.postMessage({ port: server.address().port })\n    })\n  })\n}\n\n// ── Client / tests (runs in main thread) ────────────────────────────────────\n\nfunction startServer () {\n  return new Promise((resolve) => {\n    const worker = new Worker(__filename)\n    worker.once('message', ({ port }) => resolve({ worker, port }))\n  })\n}\n\nfunction makeDispatcher (connections, maxConcurrentStreams) {\n  return new Agent({\n    keepAliveTimeout: 20_000,\n    keepAliveMaxTimeout: 60_000,\n    bodyTimeout: 20_000,\n    headersTimeout: 20_000,\n    allowH2: true,\n    connections,\n    pipelining: maxConcurrentStreams,\n    maxConcurrentStreams,\n    connect: { rejectUnauthorized: false }\n  }).compose(interceptors.responseError())\n}\n\n// Integration test:  TLS H2 server in a worker thread, slow responses (2.5s),\n// 20 connections x 100 streams = 2000 concurrent max, 10k total requests queued, responseError interceptor active.\ntest('h2: no crash when data arrives after stream abort under high concurrency', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const { worker, port } = await startServer()\n  after(() => worker.terminate())\n\n  const connections = 20\n  const maxConcurrentStreams = 100\n  const dispatcher = makeDispatcher(connections, maxConcurrentStreams)\n  after(() => dispatcher.close())\n\n  const origin = `https://127.0.0.1:${port}`\n  const count = 10_000\n\n  const requests = Array.from({ length: count }, () =>\n    dispatcher.request({\n      origin,\n      path: '/slow?delayMs=2500',\n      method: 'GET'\n    }).then((res) => res.body.dump())\n  )\n\n  const results = await Promise.allSettled(requests)\n\n  const errors = results.filter((r) => r.status === 'rejected')\n  const successCount = results.length - errors.length\n\n  if (errors.length > 0) {\n    const groups = new Map()\n    for (const e of errors) {\n      const msg = e.reason?.message ?? String(e.reason)\n      groups.set(msg, (groups.get(msg) ?? 0) + 1)\n    }\n    console.log('Error breakdown:', Object.fromEntries(groups))\n    console.log('First error stack:\\n', errors[0].reason?.stack)\n  }\n\n  // If the bug is present, some requests crash with:\n  // \"Cannot read properties of null (reading 'push')\"\n  t.ok(successCount + errors.length === count, `all ${count} requests settled`)\n  t.ok(successCount === count, `all ${count} requests succeeded (got ${errors.length} errors)`)\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-803.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('https://github.com/nodejs/undici/issues/803', { timeout: 60000 }, async (t) => {\n  t = tspl(t, { plan: 2 })\n  const SIZE = 5900373096\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    const chunkSize = res.writableHighWaterMark << 5\n    const parts = (SIZE / chunkSize) | 0\n    const lastPartSize = SIZE % chunkSize\n    const chunk = Buffer.allocUnsafe(chunkSize)\n\n    res.shouldKeepAlive = false\n    res.setHeader('content-length', SIZE)\n    let i = 0\n\n    while (i++ < parts) {\n      if (res.write(chunk) === false) {\n        await once(res, 'drain')\n      }\n    }\n    if (res.write(chunk.subarray(0, lastPartSize)) === false) {\n      await once(res, 'drain')\n    }\n\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n\n    let pos = 0\n    data.body.on('data', (buf) => {\n      pos += buf.length\n    })\n    data.body.on('end', () => {\n      t.strictEqual(pos, SIZE)\n    })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/issue-810.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client, errors } = require('..')\nconst net = require('node:net')\n\ntest('https://github.com/mcollina/undici/issues/810', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  let x = 0\n  const server = net.createServer(socket => {\n    if (x++ === 0) {\n      socket.write('HTTP/1.1 200 OK\\r\\n')\n      socket.write('Content-Length: 1\\r\\n\\r\\n')\n      socket.write('11111\\r\\n')\n    } else {\n      socket.write('HTTP/1.1 200 OK\\r\\n')\n      socket.write('Content-Length: 0\\r\\n\\r\\n')\n      socket.write('\\r\\n')\n    }\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`, { pipelining: 2 })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      // t.fail() FIX: Should fail.\n      t.ok(true, 'pass')\n    }).on('error', err => (\n      t.ok(err instanceof errors.HTTPParserError)\n    ))\n  })\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ok(err instanceof errors.HTTPParserError)\n  })\n  await t.completed\n})\n\ntest('https://github.com/mcollina/undici/issues/810 no pipelining', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 1\\r\\n\\r\\n')\n    socket.write('11111\\r\\n')\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      // t.fail() FIX: Should fail.\n      t.ok(true, 'pass')\n    })\n  })\n  await t.completed\n})\n\ntest('https://github.com/mcollina/undici/issues/810 pipelining', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 1\\r\\n\\r\\n')\n    socket.write('11111\\r\\n')\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, { pipelining: true })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      // t.fail() FIX: Should fail.\n      t.ok(true, 'pass')\n    })\n  })\n  await t.completed\n})\n\ntest('https://github.com/mcollina/undici/issues/810 pipelining 2', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Content-Length: 1\\r\\n\\r\\n')\n    socket.write('11111\\r\\n')\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n\n  const client = new Client(`http://localhost:${server.address().port}`, { pipelining: true })\n  after(() => client.close())\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n    data.body.resume().on('end', () => {\n      // t.fail() FIX: Should fail.\n      t.ok(true, 'pass')\n    })\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ok(err instanceof errors.HTTPParserError)\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/jest/instanceof-error.test.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\n/* global expect, it, jest, AbortController */\n\n// https://github.com/facebook/jest/issues/11607#issuecomment-899068995\njest.useRealTimers()\n\nit('isErrorLike sanity check', () => {\n  const { isErrorLike } = require('../../lib/web/fetch/util')\n  const error = new DOMException('')\n\n  // https://github.com/facebook/jest/issues/2549\n  expect(error instanceof Error).toBeFalsy()\n  expect(isErrorLike(error)).toBeTruthy()\n})\n\nit('Real use-case', async () => {\n  const { fetch } = require('../..')\n\n  const ac = new AbortController()\n  ac.abort()\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  }).listen(0)\n\n  await once(server, 'listening')\n\n  const promise = fetch(`https://localhost:${server.address().port}`, {\n    signal: ac.signal\n  })\n\n  await expect(async () => await promise).rejects.toThrow(/^Th(e|is) operation was aborted\\.?$/)\n\n  server.close()\n  await once(server, 'close')\n})\n"
  },
  {
    "path": "test/jest/issue-1757.test.js",
    "content": "'use strict'\n\nconst { Dispatcher, setGlobalDispatcher, MockAgent } = require('../..')\n\n/* global expect, it */\n\nclass MiniflareDispatcher extends Dispatcher {\n  constructor (inner, options) {\n    super(options)\n    this.inner = inner\n  }\n\n  dispatch (options, handler) {\n    return this.inner.dispatch(options, handler)\n  }\n\n  close (...args) {\n    return this.inner.close(...args)\n  }\n\n  destroy (...args) {\n    return this.inner.destroy(...args)\n  }\n}\n\nit('https://github.com/nodejs/undici/issues/1757', async () => {\n  // fetch isn't exported in <16.8\n  const { fetch } = require('../..')\n\n  const mockAgent = new MockAgent()\n  const mockClient = mockAgent.get('http://localhost:3000')\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(new MiniflareDispatcher(mockAgent))\n\n  mockClient.intercept({\n    path: () => true,\n    method: () => true\n  }).reply(200, async (opts) => {\n    if (opts.body?.[Symbol.asyncIterator]) {\n      const chunks = []\n      for await (const chunk of opts.body) {\n        chunks.push(chunk)\n      }\n\n      return Buffer.concat(chunks)\n    }\n\n    return opts.body\n  })\n\n  const response = await fetch('http://localhost:3000', {\n    method: 'POST',\n    body: JSON.stringify({ foo: 'bar' })\n  })\n\n  expect(response.json()).resolves.toMatchObject({ foo: 'bar' })\n  expect(response.status).toBe(200)\n})\n"
  },
  {
    "path": "test/jest/mock-agent.test.js",
    "content": "'use strict'\n\nconst { request, setGlobalDispatcher, MockAgent } = require('../..')\nconst { getResponse } = require('../../lib/mock/mock-utils')\n\n/* global describe, it, afterEach, expect */\n\ndescribe('MockAgent', () => {\n  let mockAgent\n\n  afterEach(() => {\n    mockAgent.close()\n  })\n\n  it('should work in jest', async () => {\n    expect.assertions(4)\n\n    const baseUrl = 'http://localhost:9999'\n\n    mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    const mockClient = mockAgent.get(baseUrl)\n\n    mockClient.intercept({\n      path: '/foo?hello=there&see=ya',\n      method: 'POST',\n      body: 'form1=data1&form2=data2'\n    }).reply(200, { foo: 'bar' }, {\n      headers: {\n        'content-type': 'application/json'\n      },\n      trailers: { 'Content-MD5': 'test' }\n    })\n\n    const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n      method: 'POST',\n      body: 'form1=data1&form2=data2'\n    })\n    expect(statusCode).toBe(200)\n    expect(headers).toEqual({ 'content-type': 'application/json' })\n    expect(trailers).toEqual({ 'content-md5': 'test' })\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    expect(jsonResponse).toEqual({ foo: 'bar' })\n  })\n})\n\ndescribe('MockAgent with ignoreTrailingSlash option', () => {\n  const trailingSlashUrl = 'http://localhost:9999/'\n  const noTrailingSlashUrl = 'http://localhost:9999'\n\n  it('should not remove trailing slash from origin if the option is not enable', async () => {\n    const mockClient = new MockAgent()\n\n    const dispatcherOne = mockClient.get(trailingSlashUrl)\n    const dispatcherTwo = mockClient.get(noTrailingSlashUrl)\n\n    expect(dispatcherOne).not.toBe(dispatcherTwo)\n  })\n\n  it('should remove trailing slash from origin if enabled the option', async () => {\n    const mockClient = new MockAgent({ ignoreTrailingSlash: true })\n\n    const dispatcherOne = mockClient.get(trailingSlashUrl)\n    const dispatcherTwo = mockClient.get(noTrailingSlashUrl)\n\n    expect(dispatcherOne).toBe(dispatcherTwo)\n  })\n})\n"
  },
  {
    "path": "test/jest/mock-scope.test.js",
    "content": "'use strict'\n\nconst { MockAgent, setGlobalDispatcher, request } = require('../../index')\n\n/* global afterAll, expect, it, AbortController */\n\nconst mockAgent = new MockAgent()\n\nafterAll(async () => {\n  await mockAgent.close()\n})\n\nit('Jest works with MockScope.delay - issue #1327', async () => {\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n\n  const mockPool = mockAgent.get('http://localhost:3333')\n\n  mockPool.intercept({\n    path: '/jest-bugs',\n    method: 'GET'\n  }).reply(200, 'Hello').delay(100)\n\n  const ac = new AbortController()\n  setTimeout(() => ac.abort(), 5)\n  const promise = request('http://localhost:3333/jest-bugs', {\n    signal: ac.signal\n  })\n\n  await expect(async () => await promise).rejects.toThrow('This operation was aborted')\n}, 1000)\n"
  },
  {
    "path": "test/jest/parser-timeout.test.js",
    "content": "'use strict'\r\n/* global jest, describe, it, beforeEach, afterEach, expect */\r\n\r\n// test/jest/parser-timeout.test.js\r\nconst EventEmitter = require('node:events')\r\nconst connectH1 = require('../../lib/dispatcher/client-h1')\r\nconst { kParser } = require('../../lib/core/symbols')\r\n\r\n// DummySocket extends EventEmitter to support .on/.off/.read()\r\nclass DummySocket extends EventEmitter {\r\n  constructor () {\r\n    super()\r\n    this.destroyed = false\r\n    this.errored = null\r\n  }\r\n\r\n  read () {\r\n    return null\r\n  }\r\n}\r\n\r\nconst dummyClient = {\r\n  [Symbol.for('kMaxHeadersSize')]: 1024,\r\n  [Symbol.for('kMaxResponseSize')]: 1024,\r\n  [Symbol.for('kQueue')]: [],\r\n  [Symbol.for('kRunningIdx')]: 0,\r\n  headersTimeout: 100,\r\n  bodyTimeout: 100\r\n}\r\n\r\ndescribe('Parser#setTimeout under fake timers', () => {\r\n  beforeEach(() => jest.useFakeTimers('modern'))\r\n  afterEach(() => jest.useRealTimers())\r\n\r\n  it('does not throw when calling setTimeout under fake timers', async () => {\r\n    const socket = new DummySocket()\r\n    await connectH1(dummyClient, socket) // connectH1 creates Parser -> socket[kParser]\r\n    const parser = socket[kParser]\r\n    expect(() => parser.setTimeout(200, 0)).not.toThrow()\r\n  })\r\n})\r\n"
  },
  {
    "path": "test/jest/test.js",
    "content": "'use strict'\n\nconst { Client } = require('../..')\nconst { createServer } = require('node:http')\n/* global test, expect */\n\ntest('should work in jest', async () => {\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    expect(req.url).toBe('/')\n    expect(req.method).toBe('POST')\n    expect(req.headers.host).toBe(`localhost:${server.address().port}`)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n  await expect(new Promise((resolve, reject) => {\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json'\n        },\n        body: '{}'\n      }, (err, result) => {\n        server.close()\n        client.close()\n        if (err) {\n          reject(err)\n        } else {\n          resolve(result.body.text())\n        }\n      })\n    })\n  })).resolves.toBe('hello')\n})\n"
  },
  {
    "path": "test/jest/util-timers.test.js",
    "content": "'use strict'\r\n/* global jest, describe, it, beforeEach, afterEach, expect */\r\n\r\n// test/jest/util-timers.test.js\r\nconst timers = require('../../lib/util/timers')\r\n\r\ndescribe('util/timers under fake timers', () => {\r\n  beforeEach(() => jest.useFakeTimers('modern'))\r\n  afterEach(() => {\r\n    jest.useRealTimers()\r\n    try {\r\n      timers.reset()\r\n    } catch {}\r\n  })\r\n\r\n  it('setFastTimeout + clearFastTimeout does not throw', () => {\r\n    const fast = timers.setFastTimeout(() => {}, 2000)\r\n    expect(typeof fast.refresh).toBe('function')\r\n    expect(() => timers.clearFastTimeout(fast)).not.toThrow()\r\n  })\r\n\r\n  it('short setTimeout + clearTimeout does not throw', () => {\r\n    const t = timers.setTimeout(() => {}, 10)\r\n    expect(() => timers.clearTimeout(t)).not.toThrow()\r\n  })\r\n})\r\n"
  },
  {
    "path": "test/max-headers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\ntest('handle a lot of headers', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const headers = {}\n  for (let n = 0; n < 64; ++n) {\n    headers[n] = String(n)\n  }\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, headers)\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    t.ifError(err)\n    const headers2 = {}\n    for (let n = 0; n < 64; ++n) {\n      headers2[n] = data.headers[n]\n    }\n    t.deepStrictEqual(headers2, headers)\n    data.body\n      .resume()\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/max-response-size.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after, describe } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\n\ndescribe('max response size', async (t) => {\n  test('default max default size should allow all responses', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.on('request', (req, res) => {\n      res.end('hello')\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, { maxResponseSize: -1 })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, body }) => {\n        t.ifError(err)\n        t.strictEqual(statusCode, 200)\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    })\n\n    await t.completed\n  })\n\n  test('max response size set to zero should allow only empty responses', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.on('request', (req, res) => {\n      res.end()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, { maxResponseSize: 0 })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, body }) => {\n        t.ifError(err)\n        t.strictEqual(statusCode, 200)\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          t.strictEqual('', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    })\n\n    await t.completed\n  })\n\n  test('should throw an error if the response is too big', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.on('request', (req, res) => {\n      res.end('hello')\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        maxResponseSize: 1\n      })\n\n      after(() => client.close())\n\n      client.request({ path: '/', method: 'GET' }, (err, { body }) => {\n        t.ifError(err)\n        body.on('error', (err) => {\n          t.ok(err)\n          t.ok(err instanceof errors.ResponseExceededMaxSizeError)\n        })\n      })\n    })\n\n    await t.completed\n  })\n\n  test('invalid max response size should throw an error', async (t) => {\n    t = tspl(t, { plan: 2 })\n\n    t.throws(() => {\n      // eslint-disable-next-line no-new\n      new Client('http://localhost:3000', { maxResponseSize: 'hello' })\n    }, 'maxResponseSize must be a number')\n    t.throws(() => {\n      // eslint-disable-next-line no-new\n      new Client('http://localhost:3000', { maxResponseSize: -2 })\n    }, 'maxResponseSize must be greater than or equal to -1')\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/mock-agent.js",
    "content": "'use strict'\n\nconst { test, after, describe } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { request, setGlobalDispatcher, MockAgent, Agent } = require('..')\nconst { getResponse } = require('../lib/mock/mock-utils')\nconst { kClients, kConnected } = require('../lib/core/symbols')\nconst { InvalidArgumentError, ClientDestroyedError } = require('../lib/core/errors')\nconst MockClient = require('../lib/mock/mock-client')\nconst MockPool = require('../lib/mock/mock-pool')\nconst { kAgent, kMockAgentIsCallHistoryEnabled } = require('../lib/mock/mock-symbols')\nconst Dispatcher = require('../lib/dispatcher/dispatcher')\nconst { MockNotMatchedError } = require('../lib/mock/mock-errors')\nconst { fetch } = require('..')\nconst { MockCallHistory } = require('../lib/mock/mock-call-history')\n\ndescribe('MockAgent - constructor', () => {\n  test('sets up mock agent', t => {\n    t.plan(1)\n    t.assert.doesNotThrow(() => new MockAgent())\n  })\n\n  test('should implement the Dispatcher API', t => {\n    t.plan(1)\n\n    const mockAgent = new MockAgent()\n    t.assert.ok(mockAgent instanceof Dispatcher)\n  })\n\n  test('sets up mock agent with single connection', t => {\n    t.plan(1)\n    t.assert.doesNotThrow(() => new MockAgent({ connections: 1 }))\n  })\n\n  test('should error passed agent is not valid', t => {\n    t.plan(2)\n    t.assert.throws(() => new MockAgent({ agent: {} }), new InvalidArgumentError('Argument opts.agent must implement Agent'))\n    t.assert.throws(() => new MockAgent({ agent: { dispatch: '' } }), new InvalidArgumentError('Argument opts.agent must implement Agent'))\n  })\n\n  test('should be able to specify the agent to mock', t => {\n    t.plan(1)\n    const agent = new Agent()\n    after(() => agent.close())\n    const mockAgent = new MockAgent({ agent })\n\n    t.assert.strictEqual(mockAgent[kAgent], agent)\n  })\n\n  test('should disable call history by default', t => {\n    t.plan(2)\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    t.assert.strictEqual(mockAgent[kMockAgentIsCallHistoryEnabled], false)\n    t.assert.strictEqual(mockAgent.getCallHistory(), undefined)\n  })\n\n  test('should enable call history if option is true', t => {\n    t.plan(2)\n    const mockAgent = new MockAgent({ enableCallHistory: true })\n    after(() => mockAgent.close())\n\n    t.assert.strictEqual(mockAgent[kMockAgentIsCallHistoryEnabled], true)\n    t.assert.ok(mockAgent.getCallHistory() instanceof MockCallHistory)\n  })\n\n  test('should disable call history if option is false', t => {\n    t.plan(2)\n    after(() => mockAgent.close())\n    const mockAgent = new MockAgent({ enableCallHistory: false })\n\n    t.assert.strictEqual(mockAgent[kMockAgentIsCallHistoryEnabled], false)\n    t.assert.strictEqual(mockAgent.getCallHistory(), undefined)\n  })\n\n  test('should throw if enableCallHistory option is not a boolean', t => {\n    t.plan(1)\n\n    t.assert.throws(() => new MockAgent({ enableCallHistory: 'hello' }), new InvalidArgumentError('options.enableCallHistory must to be a boolean'))\n  })\n})\n\ndescribe('MockAgent - enableCallHistory', () => {\n  test('should enable call history and add call history log', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get('http://localhost:9999')\n    mockClient.intercept({\n      path: '/foo',\n      method: 'GET'\n    }).reply(200, 'foo').persist()\n\n    await fetch('http://localhost:9999/foo')\n\n    t.assert.strictEqual(mockAgent.getCallHistory()?.calls()?.length, undefined)\n\n    mockAgent.enableCallHistory()\n\n    await request('http://localhost:9999/foo')\n\n    t.assert.strictEqual(mockAgent.getCallHistory()?.calls()?.length, 1)\n  })\n})\n\ndescribe('MockAgent - disableCallHistory', () => {\n  test('should disable call history and not add call history log', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent({ enableCallHistory: true })\n    setGlobalDispatcher(mockAgent)\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get('http://localhost:9999')\n    mockClient.intercept({\n      path: '/foo',\n      method: 'GET'\n    }).reply(200, 'foo').persist()\n\n    await request('http://localhost:9999/foo')\n\n    t.assert.strictEqual(mockAgent.getCallHistory()?.calls()?.length, 1)\n\n    mockAgent.disableCallHistory()\n\n    await request('http://localhost:9999/foo')\n\n    t.assert.strictEqual(mockAgent.getCallHistory()?.calls()?.length, 1)\n  })\n})\n\ndescribe('MockAgent - get', () => {\n  test('should return MockClient', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get(baseUrl)\n    t.assert.ok(mockClient instanceof MockClient)\n  })\n\n  test('should return MockPool', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n    t.assert.ok(mockPool instanceof MockPool)\n  })\n\n  test('should return the same instance if already created', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool1 = mockAgent.get(baseUrl)\n    const mockPool2 = mockAgent.get(baseUrl)\n    t.assert.strictEqual(mockPool1, mockPool2)\n  })\n})\n\ndescribe('MockAgent - dispatch', () => {\n  test('should call the dispatch method of the MockPool', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    mockPool.intercept({\n      path: '/foo',\n      method: 'GET'\n    }).reply(200, 'hello')\n\n    t.assert.doesNotThrow(() => mockAgent.dispatch({\n      origin: baseUrl,\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => resume(),\n      onData: () => {},\n      onComplete: () => {},\n      onError: () => {}\n    }))\n  })\n\n  test('should call the dispatch method of the MockClient', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get(baseUrl)\n\n    mockClient.intercept({\n      path: '/foo',\n      method: 'GET'\n    }).reply(200, 'hello')\n\n    t.assert.doesNotThrow(() => mockAgent.dispatch({\n      origin: baseUrl,\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => resume(),\n      onData: () => {},\n      onComplete: () => {},\n      onError: () => {}\n    }))\n  })\n})\n\ntest('MockAgent - .close should clean up registered pools', async (t) => {\n  t.plan(5)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent()\n\n  // Register a pool\n  const mockPool = mockAgent.get(baseUrl)\n  t.assert.ok(mockPool instanceof MockPool)\n\n  t.assert.strictEqual(mockPool[kConnected], 1)\n  t.assert.strictEqual(mockAgent[kClients].size, 1)\n  await mockAgent.close()\n  t.assert.strictEqual(mockPool[kConnected], 0)\n  t.assert.strictEqual(mockAgent[kClients].size, 0)\n})\n\ntest('MockAgent - .close should clean up registered clients', async (t) => {\n  t.plan(5)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent({ connections: 1 })\n\n  // Register a pool\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n\n  t.assert.strictEqual(mockClient[kConnected], 1)\n  t.assert.strictEqual(mockAgent[kClients].size, 1)\n  await mockAgent.close()\n  t.assert.strictEqual(mockClient[kConnected], 0)\n  t.assert.strictEqual(mockAgent[kClients].size, 0)\n})\n\ntest('MockAgent - [kClients] should match encapsulated agent', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const agent = new Agent()\n  after(() => agent.close())\n\n  const mockAgent = new MockAgent({ agent })\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  // The MockAgent should encapsulate the input agent clients\n  t.assert.strictEqual(mockAgent[kClients].size, agent[kClients].size)\n})\n\ntest('MockAgent - basic intercept with MockAgent.request', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: { 'content-type': 'application/json' },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await mockAgent.request({\n    origin: baseUrl,\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockAgent - basic intercept with request', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: { 'content-type': 'application/json' },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockAgent - should support local agents', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: {\n      'content-type': 'application/json'\n    },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n    method: 'POST',\n    body: 'form1=data1&form2=data2',\n    dispatcher: mockAgent\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockAgent - should support specifying custom agents to mock', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const agent = new Agent()\n  after(() => agent.close())\n\n  const mockAgent = new MockAgent({ agent })\n  setGlobalDispatcher(mockAgent)\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: {\n      'content-type': 'application/json'\n    },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockAgent - basic Client intercept with request', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: {\n      'content-type': 'application/json'\n    },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockAgent - basic intercept with multiple pools', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n  const mockPool1 = mockAgent.get(baseUrl)\n  const mockPool2 = mockAgent.get('http://localhost:9999')\n\n  mockPool1.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar-1' }, {\n    headers: {\n      'content-type': 'application/json'\n    },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  mockPool2.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'GET',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar-2' })\n\n  const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar-1'\n  })\n})\n\ntest('MockAgent - should handle multiple responses for an interceptor', async (t) => {\n  t.plan(6)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n\n  const interceptor = mockPool.intercept({\n    path: '/foo',\n    method: 'POST'\n  })\n  interceptor.reply(200, { foo: 'bar' }, {\n    headers: {\n      'content-type': 'application/json'\n    }\n  })\n  interceptor.reply(200, { hello: 'there' }, {\n    headers: {\n      'content-type': 'application/json'\n    }\n  })\n\n  {\n    const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n      method: 'POST'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'application/json')\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, {\n      foo: 'bar'\n    })\n  }\n\n  {\n    const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n      method: 'POST'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'application/json')\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, {\n      hello: 'there'\n    })\n  }\n})\n\ntest('MockAgent - should call original Pool dispatch if request not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - should call original Client dispatch if request not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - should handle string responses', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'POST'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'POST'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - should handle basic concurrency for requests', { jobs: 5 }, async (t) => {\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  await Promise.all([...Array(5).keys()].map(idx =>\n    test(`concurrent job (${idx})`, async (t) => {\n      t.plan(2)\n\n      const baseUrl = 'http://localhost:9999'\n\n      const mockPool = mockAgent.get(baseUrl)\n      mockPool.intercept({\n        path: '/foo',\n        method: 'POST'\n      }).reply(200, { foo: `bar ${idx}` })\n\n      const { statusCode, body } = await request(`${baseUrl}/foo`, {\n        method: 'POST'\n      })\n      t.assert.strictEqual(statusCode, 200)\n\n      const jsonResponse = JSON.parse(await getResponse(body))\n      t.assert.deepStrictEqual(jsonResponse, {\n        foo: `bar ${idx}`\n      })\n    })\n  ))\n})\n\ntest('MockAgent - handle delays to simulate work', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'POST'\n  }).reply(200, 'hello').delay(50)\n\n  const start = process.hrtime()\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'POST'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n  const elapsedInMs = Math.ceil(process.hrtime(start)[1] / 1e6)\n  t.assert.ok(elapsedInMs >= 50, `Elapsed time is not greater than 50ms: ${elapsedInMs}`)\n})\n\ntest('MockAgent - should persist requests', async (t) => {\n  t.plan(8)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: {\n      'content-type': 'application/json'\n    },\n    trailers: { 'Content-MD5': 'test' }\n  }).persist()\n\n  {\n    const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n      method: 'POST',\n      body: 'form1=data1&form2=data2'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'application/json')\n    t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, {\n      foo: 'bar'\n    })\n  }\n\n  {\n    const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {\n      method: 'POST',\n      body: 'form1=data1&form2=data2'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'application/json')\n    t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, {\n      foo: 'bar'\n    })\n  }\n})\n\ntest('MockAgent - getCallHistory with no name parameter should return the agent call history', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get('http://localhost:9999')\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  t.assert.ok(mockAgent.getCallHistory() instanceof MockCallHistory)\n})\n\ntest('MockAgent - getCallHistory with request should return the call history instance with history log', async (t) => {\n  t.plan(9)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const baseUrl = 'http://localhost:9999'\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: /^\\/foo/,\n    method: 'POST'\n  }).reply(200, 'foo')\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 0)\n\n  const path = '/foo'\n  const url = new URL(path, baseUrl)\n  const method = 'POST'\n  const body = { data: 'value' }\n  const query = { a: 1 }\n  const headers = { 'content-type': 'application/json' }\n\n  await request(url, { method, query, body: JSON.stringify(body), headers })\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 1)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.body, JSON.stringify(body))\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.headers, headers)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.method, method)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.origin, baseUrl)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.path, path)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.fullUrl, `${url.toString()}?${new URLSearchParams(query).toString()}`)\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.searchParams, { a: '1' })\n})\n\ntest('MockAgent - getCallHistory with fetch should return the call history instance with history log', async (t) => {\n  t.plan(9)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const baseUrl = 'http://localhost:9999'\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: /^\\/foo/,\n    method: 'POST'\n  }).reply(200, 'foo')\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 0)\n\n  const path = '/foo'\n  const url = new URL(path, baseUrl)\n  const method = 'POST'\n  const body = { data: 'value' }\n  const query = { a: 1 }\n  url.search = new URLSearchParams(query)\n  const headers = { authorization: 'token', 'content-type': 'application/json' }\n\n  await fetch(url, { method, query, body: JSON.stringify(body), headers })\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 1)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.body, JSON.stringify(body))\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.headers, {\n    ...headers,\n    'accept-encoding': 'gzip, deflate',\n    'content-length': '16',\n    'content-type': 'application/json',\n    'accept-language': '*',\n    'sec-fetch-mode': 'cors',\n    'user-agent': 'undici',\n    accept: '*/*'\n  })\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.method, method)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.origin, baseUrl)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.path, url.pathname)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.fullUrl, url.toString())\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.searchParams, { a: '1' })\n})\n\ntest('MockAgent - getCallHistory with fetch with a minimal configuration should register call history log', async (t) => {\n  t.plan(11)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const baseUrl = 'http://localhost:9999'\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: '/'\n  }).reply(200, 'foo')\n\n  const path = '/'\n  const url = new URL(path, baseUrl)\n\n  await fetch(url)\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 1)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.body, null)\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.headers, {\n    'accept-encoding': 'gzip, deflate',\n    'accept-language': '*',\n    'sec-fetch-mode': 'cors',\n    'user-agent': 'undici',\n    accept: '*/*'\n  })\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.method, 'GET')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.origin, baseUrl)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.path, path)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.fullUrl, baseUrl + path)\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.searchParams, {})\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.host, 'localhost:9999')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.port, '9999')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.protocol, 'http:')\n})\n\ntest('MockAgent - getCallHistory with request with a minimal configuration should register call history log', async (t) => {\n  t.plan(11)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const baseUrl = 'http://localhost:9999'\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: '/'\n  }).reply(200, 'foo')\n\n  const path = '/'\n  const url = new URL(path, baseUrl)\n\n  await request(url)\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 1)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.body, undefined)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.headers, undefined)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.method, 'GET')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.origin, baseUrl)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.path, path)\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.fullUrl, baseUrl + path)\n  t.assert.deepStrictEqual(mockAgent.getCallHistory()?.lastCall()?.searchParams, {})\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.host, 'localhost:9999')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.port, '9999')\n  t.assert.strictEqual(mockAgent.getCallHistory()?.lastCall()?.protocol, 'http:')\n})\n\ntest('MockAgent - clearCallHistory should clear call history logs', async (t) => {\n  t.plan(3)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const baseUrl = 'http://localhost:9999'\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: /^\\/foo/,\n    method: 'POST'\n  }).reply(200, 'foo').persist()\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 0)\n\n  const path = '/foo'\n  const url = new URL(path, baseUrl)\n  const method = 'POST'\n  const body = { data: 'value' }\n  const query = { a: 1 }\n  const headers = { 'content-type': 'application/json' }\n\n  await request(url, { method, query, body: JSON.stringify(body), headers })\n  await request(url, { method, query, body: JSON.stringify(body), headers })\n  await request(url, { method, query, body: JSON.stringify(body), headers })\n  await request(url, { method, query, body: JSON.stringify(body), headers })\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 4)\n\n  mockAgent.clearCallHistory()\n\n  t.assert.ok(mockAgent.getCallHistory()?.calls().length === 0)\n})\n\ntest('MockAgent - handle persists with delayed requests', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'POST'\n  }).reply(200, 'hello').delay(1).persist()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'POST'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'hello')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'POST'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'hello')\n  }\n})\n\ntest('MockAgent - calling close on a mock pool should not affect other mock pools', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPoolToClose = mockAgent.get('http://localhost:9999')\n  mockPoolToClose.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'should-not-be-returned')\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n  mockPool.intercept({\n    path: '/bar',\n    method: 'POST'\n  }).reply(200, 'bar')\n\n  await mockPoolToClose.close()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/bar`, {\n      method: 'POST'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'bar')\n  }\n})\n\ntest('MockAgent - close removes all registered mock clients', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  setGlobalDispatcher(mockAgent)\n\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  await mockAgent.close()\n  t.assert.strictEqual(mockAgent[kClients].size, 0)\n\n  try {\n    await request(`${baseUrl}/foo`, { method: 'GET' })\n  } catch (err) {\n    t.assert.ok(err instanceof ClientDestroyedError)\n  }\n})\n\ntest('MockAgent - close clear all registered mock call history logs', async (t) => {\n  t.plan(2)\n\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  setGlobalDispatcher(mockAgent)\n\n  const mockClient = mockAgent.get('http://localhost:9999')\n\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  await request('http://localhost:9999/foo')\n\n  t.assert.strictEqual(mockAgent.getCallHistory().calls().length, 1)\n\n  await mockAgent.close()\n\n  t.assert.strictEqual(mockAgent.getCallHistory().calls().length, 0)\n})\n\ntest('MockAgent - close removes all registered mock pools', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  await mockAgent.close()\n  t.assert.strictEqual(mockAgent[kClients].size, 0)\n\n  try {\n    await request(`${baseUrl}/foo`, { method: 'GET' })\n  } catch (err) {\n    t.assert.ok(err instanceof ClientDestroyedError)\n  }\n})\n\ntest('MockAgent - should handle replyWithError', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).replyWithError(new Error('kaboom'))\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, { method: 'GET' }), new Error('kaboom'))\n})\n\ntest('MockAgent - should support setting a reply to respond a set amount of times', async (t) => {\n  t.plan(9)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo').times(2)\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`)\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`)\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, headers, body } = await request(`${baseUrl}/foo`)\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'hello')\n  }\n})\n\ntest('MockAgent - persist overrides times', async (t) => {\n  t.plan(6)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo').times(2).persist()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n})\n\ntest('MockAgent - matcher should not find mock dispatch if path is of unsupported type', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: {},\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - should match path with regex', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: /foo/,\n    method: 'GET'\n  }).reply(200, 'foo').persist()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/hello/foobar`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n})\n\ntest('MockAgent - should match path with function', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: (value) => value === '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match method with regex', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: /^GET$/\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match method with function', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: (value) => value === 'GET'\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match body with regex', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    body: /hello/\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    body: 'hello=there'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match body with function', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    body: (value) => value.startsWith('hello')\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    body: 'hello=there'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match headers with string', async (t) => {\n  t.plan(6)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers: {\n      'User-Agent': 'undici',\n      Host: 'example.com'\n    }\n  }).reply(200, 'foo')\n\n  // Disable net connect so we can make sure it matches properly\n  mockAgent.disableNetConnect()\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET'\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'wrong'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'example.com'\n    }\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match headers with regex', async (t) => {\n  t.plan(6)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers: {\n      'User-Agent': /^undici$/,\n      Host: /^example.com$/\n    }\n  }).reply(200, 'foo')\n\n  // Disable net connect so we can make sure it matches properly\n  mockAgent.disableNetConnect()\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET'\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'wrong'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'example.com'\n    }\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match headers with function', async (t) => {\n  t.plan(6)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers: {\n      'User-Agent': (value) => value === 'undici',\n      Host: (value) => value === 'example.com'\n    }\n  }).reply(200, 'foo')\n\n  // Disable net connect so we can make sure it matches properly\n  mockAgent.disableNetConnect()\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET'\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'wrong'\n    }\n  }), MockNotMatchedError, 'should reject with MockNotMatchedError')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      foo: 'bar',\n      'User-Agent': 'undici',\n      Host: 'example.com'\n    }\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match url with regex', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(new RegExp(baseUrl))\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - should match url with function', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get((value) => baseUrl === value)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - handle default reply headers', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).defaultReplyHeaders({ foo: 'bar' }).reply(200, 'foo', { headers: { hello: 'there' } })\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.deepStrictEqual(headers, {\n    foo: 'bar',\n    hello: 'there'\n  })\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - handle default reply trailers', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).defaultReplyTrailers({ foo: 'bar' }).reply(200, 'foo', { trailers: { hello: 'there' } })\n\n  const { statusCode, trailers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.deepStrictEqual(trailers, {\n    foo: 'bar',\n    hello: 'there'\n  })\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - return calculated content-length if specified', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).replyContentLength().reply(200, 'foo', { headers: { hello: 'there' } })\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.deepStrictEqual(headers, {\n    hello: 'there',\n    'content-length': '3'\n  })\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'foo')\n})\n\ntest('MockAgent - return calculated content-length for object response if specified', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).replyContentLength().reply(200, { foo: 'bar' }, { headers: { hello: 'there' } })\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.deepStrictEqual(headers, {\n    hello: 'there',\n    'content-length': '13'\n  })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, { foo: 'bar' })\n})\n\ntest('MockAgent - should activate and deactivate mock clients', async (t) => {\n  t.plan(9)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo').persist()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n\n  mockAgent.deactivate()\n\n  {\n    const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'hello')\n  }\n\n  mockAgent.activate()\n\n  {\n    const { statusCode, body } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n\n    const response = await getResponse(body)\n    t.assert.strictEqual(response, 'foo')\n  }\n})\n\ntest('MockAgent - enableNetConnect should allow all original dispatches to be called if dispatch not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect()\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - enableNetConnect with a host string should allow all original dispatches to be called if mockDispatch not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect(`localhost:${server.address().port}`)\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - enableNetConnect when called with host string multiple times should allow all original dispatches to be called if mockDispatch not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect('example.com:9999')\n  mockAgent.enableNetConnect(`localhost:${server.address().port}`)\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - enableNetConnect with a host regex should allow all original dispatches to be called if mockDispatch not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect(new RegExp(`localhost:${server.address().port}`))\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - enableNetConnect with a function should allow all original dispatches to be called if mockDispatch not found', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect((value) => value === `localhost:${server.address().port}`)\n\n  const { statusCode, headers, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'text/plain')\n\n  const response = await getResponse(body)\n  t.assert.strictEqual(response, 'hello')\n})\n\ntest('MockAgent - enableNetConnect with an unknown input should throw', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get('http://localhost:9999')\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  t.assert.throws(() => mockAgent.enableNetConnect({}), new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.'))\n})\n\ntest('MockAgent - enableNetConnect should throw if dispatch not matched for path and the origin was not allowed by net connect', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect('example.com:9999')\n\n  await t.assert.rejects(request(`${baseUrl}/wrong`, {\n    method: 'GET'\n  }), new MockNotMatchedError(`Mock dispatch not matched for path '/wrong': subsequent request to origin ${baseUrl} was not allowed (net.connect is not enabled for this origin)`))\n})\n\ntest('MockAgent - enableNetConnect should throw if dispatch not matched for method and the origin was not allowed by net connect', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect('example.com:9999')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'WRONG'\n  }), new MockNotMatchedError(`Mock dispatch not matched for method 'WRONG' on path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect is not enabled for this origin)`))\n})\n\ntest('MockAgent - enableNetConnect should throw if dispatch not matched for body and the origin was not allowed by net connect', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    body: 'hello'\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect('example.com:9999')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    body: 'wrong'\n  }), new MockNotMatchedError(`Mock dispatch not matched for body 'wrong' on path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect is not enabled for this origin)`))\n})\n\ntest('MockAgent - enableNetConnect should throw if dispatch not matched for headers and the origin was not allowed by net connect', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers: {\n      'User-Agent': 'undici'\n    }\n  }).reply(200, 'foo')\n\n  mockAgent.enableNetConnect('example.com:9999')\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      'User-Agent': 'wrong'\n    }\n  }), new MockNotMatchedError(`Mock dispatch not matched for headers '{\"User-Agent\":\"wrong\"}' on path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect is not enabled for this origin)`))\n})\n\ntest('MockAgent - disableNetConnect should throw if dispatch not found by net connect', async (t) => {\n  t.plan(1)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual(req.url, '/foo')\n    t.assert.strictEqual(req.method, 'GET')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  mockPool.intercept({\n    path: '/wrong',\n    method: 'GET'\n  }).reply(200, 'foo')\n\n  mockAgent.disableNetConnect()\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET'\n  }), new MockNotMatchedError(`Mock dispatch not matched for path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect disabled)`))\n})\n\ntest('MockAgent - headers function interceptor', async (t) => {\n  t.plan(8)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n\n  // Disable net connect so we can make sure it matches properly\n  mockAgent.disableNetConnect()\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers (headers) {\n      t.assert.strictEqual(typeof headers, 'object')\n      return !Object.keys(headers).includes('authorization')\n    }\n  }).reply(200, 'foo').times(3)\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: {\n      Authorization: 'Bearer foo'\n    }\n  }), new MockNotMatchedError(`Mock dispatch not matched for headers '{\"Authorization\":\"Bearer foo\"}' on path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect disabled)`))\n\n  await t.assert.rejects(request(`${baseUrl}/foo`, {\n    method: 'GET',\n    headers: ['Authorization', 'Bearer foo']\n  }), new MockNotMatchedError(`Mock dispatch not matched for headers '[\"Authorization\",\"Bearer foo\"]' on path '/foo': subsequent request to origin ${baseUrl} was not allowed (net.connect disabled)`))\n\n  {\n    const { statusCode } = await request(`${baseUrl}/foo`, {\n      method: 'GET',\n      headers: {\n        foo: 'bar'\n      }\n    })\n    t.assert.strictEqual(statusCode, 200)\n  }\n\n  {\n    const { statusCode } = await request(`${baseUrl}/foo`, {\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n  }\n})\n\ntest('MockAgent - clients are not garbage collected', async (t) => {\n  const samples = 250\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.fail('should not be called')\n    res.end('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  // Create the dispatcher and disable net connect so we can make sure it matches properly\n  const dispatcher = new MockAgent()\n  dispatcher.disableNetConnect()\n\n  // When Node 16 is the minimum supported, this can be replaced by simply requiring setTimeout from timers/promises\n  function sleep (delay) {\n    return new Promise(resolve => {\n      setTimeout(resolve, delay)\n    })\n  }\n\n  // Purposely create the pool inside a function so that the reference is lost\n  function intercept () {\n    // Create the pool and add a lot of intercepts\n    const pool = dispatcher.get(baseUrl)\n\n    for (let i = 0; i < samples; i++) {\n      pool.intercept({\n        path: `/foo/${i}`,\n        method: 'GET'\n      }).reply(200, Buffer.alloc(1024 * 1024))\n    }\n  }\n\n  intercept()\n\n  const results = new Set()\n  for (let i = 0; i < samples; i++) {\n    // Let's make some time pass to allow garbage collection to happen\n    await sleep(10)\n\n    const { statusCode } = await request(`${baseUrl}/foo/${i}`, { method: 'GET', dispatcher })\n    results.add(statusCode)\n  }\n\n  t.assert.strictEqual(results.size, 1)\n  t.assert.ok(results.has(200))\n})\n\n// https://github.com/nodejs/undici/issues/1321\ntest('MockAgent - using fetch yields correct statusText', async (t) => {\n  t.plan(4)\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get('http://localhost:3000')\n\n  mockPool.intercept({\n    path: '/statusText',\n    method: 'GET'\n  }).reply(200, 'Body')\n\n  const { status, statusText } = await fetch('http://localhost:3000/statusText')\n\n  t.assert.strictEqual(status, 200)\n  t.assert.strictEqual(statusText, 'OK')\n\n  mockPool.intercept({\n    path: '/unknownStatusText',\n    method: 'GET'\n  }).reply(420, 'Everyday')\n\n  const unknownStatusCodeRes = await fetch('http://localhost:3000/unknownStatusText')\n  t.assert.strictEqual(unknownStatusCodeRes.status, 420)\n  t.assert.strictEqual(unknownStatusCodeRes.statusText, 'unknown')\n})\n\n// https://github.com/nodejs/undici/issues/1556\ntest('MockAgent - using fetch yields a headers object in the reply callback', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get('http://localhost:3000')\n\n  mockPool.intercept({\n    path: '/headers',\n    method: 'GET'\n  }).reply(200, (opts) => {\n    t.assert.deepStrictEqual(opts.headers, {\n      accept: '*/*',\n      'accept-language': '*',\n      'sec-fetch-mode': 'cors',\n      'user-agent': 'undici',\n      'accept-encoding': 'gzip, deflate'\n    })\n\n    return {}\n  })\n\n  await fetch('http://localhost:3000/headers', {\n    dispatcher: mockAgent\n  })\n})\n\n// https://github.com/nodejs/undici/issues/1579\ntest('MockAgent - headers in mock dispatcher intercept should be case-insensitive', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get('https://example.com')\n\n  mockPool\n    .intercept({\n      path: '/',\n      headers: {\n        authorization: 'Bearer 12345',\n        'USER-agent': 'undici'\n      }\n    })\n    .reply(200)\n\n  await fetch('https://example.com', {\n    headers: {\n      Authorization: 'Bearer 12345',\n      'user-AGENT': 'undici'\n    }\n  })\n\n  t.assert.ok(true, 'end')\n})\n\n// https://github.com/nodejs/undici/issues/1757\ntest('MockAgent - reply callback can be asynchronous', async (t) => {\n  t.plan(2)\n\n  class MiniflareDispatcher extends Dispatcher {\n    constructor (inner, options) {\n      super(options)\n      this.inner = inner\n    }\n\n    dispatch (options, handler) {\n      return this.inner.dispatch(options, handler)\n    }\n\n    close (...args) {\n      return this.inner.close(...args)\n    }\n\n    destroy (...args) {\n      return this.inner.destroy(...args)\n    }\n  }\n\n  const mockAgent = new MockAgent()\n  const mockClient = mockAgent.get('http://localhost:3000')\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(new MiniflareDispatcher(mockAgent))\n\n  after(() => mockAgent.close())\n\n  mockClient.intercept({\n    path: () => true,\n    method: () => true\n  }).reply(200, async (opts) => {\n    if (opts.body && opts.body[Symbol.asyncIterator]) {\n      const chunks = []\n      for await (const chunk of opts.body) {\n        chunks.push(chunk)\n      }\n\n      return Buffer.concat(chunks)\n    }\n\n    return opts.body\n  }).persist()\n\n  {\n    const response = await fetch('http://localhost:3000', {\n      method: 'POST',\n      body: JSON.stringify({ foo: 'bar' })\n    })\n\n    t.assert.deepStrictEqual(await response.json(), { foo: 'bar' })\n  }\n\n  {\n    const response = await fetch('http://localhost:3000', {\n      method: 'POST',\n      body: new ReadableStream({\n        start (controller) {\n          controller.enqueue(new TextEncoder().encode('{\"foo\":'))\n\n          setTimeout(() => {\n            controller.enqueue(new TextEncoder().encode('\"bar\"}'))\n            controller.close()\n          }, 100)\n        }\n      }),\n      duplex: 'half'\n    })\n\n    t.assert.deepStrictEqual(await response.json(), { foo: 'bar' })\n  }\n})\n\ntest('MockAgent - headers should be array of strings', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n\n  const mockPool = mockAgent.get('http://localhost:3000')\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'foo', {\n    headers: {\n      'set-cookie': [\n        'foo=bar',\n        'bar=baz',\n        'baz=qux'\n      ]\n    }\n  })\n\n  const { headers } = await request('http://localhost:3000/foo', {\n    method: 'GET'\n  })\n\n  t.assert.deepStrictEqual(headers['set-cookie'], [\n    'foo=bar',\n    'bar=baz',\n    'baz=qux'\n  ])\n})\n\n// https://github.com/nodejs/undici/issues/2418\ntest('MockAgent - Sending ReadableStream body', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  setGlobalDispatcher(mockAgent)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    req.pipe(res)\n  })\n\n  after(() => mockAgent.close())\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const url = `http://localhost:${server.address().port}`\n\n  const response = await fetch(url, {\n    method: 'POST',\n    body: new ReadableStream({\n      start (controller) {\n        controller.enqueue('test')\n        controller.close()\n      }\n    }),\n    duplex: 'half'\n  })\n\n  t.assert.deepStrictEqual(await response.text(), 'test')\n})\n\n// https://github.com/nodejs/undici/issues/2616\ntest('MockAgent - headers should be array of strings (fetch)', async (t) => {\n  t.plan(1)\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get('http://localhost:3000')\n\n  mockPool\n    .intercept({\n      path: '/foo',\n      method: 'GET'\n    })\n    .reply(200, 'foo', {\n      headers: {\n        'set-cookie': ['foo=bar', 'bar=baz', 'baz=qux']\n      }\n    })\n\n  const response = await fetch('http://localhost:3000/foo', {\n    method: 'GET'\n  })\n\n  t.assert.deepStrictEqual(response.headers.getSetCookie(), ['foo=bar', 'bar=baz', 'baz=qux'])\n})\n\n// https://github.com/nodejs/undici/issues/4146\n;[\n  '/foo?array=item1&array=item2',\n  '/foo?array[]=item1&array[]=item2',\n  '/foo?array=item1,item2'\n].forEach(path => {\n  test(`MockAgent - should accept non-standard multi value search parameters when acceptNonStandardSearchParameters is true \"${path}\"`, async (t) => {\n    t.plan(4)\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('content-type', 'text/plain')\n      res.end('should not be called')\n      t.assert.fail('should not be called')\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await once(server.listen(0), 'listening')\n\n    const baseUrl = `http://localhost:${server.address().port}`\n\n    const mockAgent = new MockAgent({ acceptNonStandardSearchParameters: true })\n    after(() => mockAgent.close())\n    const mockPool = mockAgent.get(baseUrl)\n\n    mockPool.intercept({\n      path: '/foo',\n      method: 'GET',\n      query: {\n        array: ['item1', 'item2']\n      }\n    }).reply(200, { foo: 'bar' }, {\n      headers: { 'content-type': 'application/json' },\n      trailers: { 'Content-MD5': 'test' }\n    })\n\n    const { statusCode, headers, trailers, body } = await mockAgent.request({\n      origin: baseUrl,\n      path,\n      method: 'GET'\n    })\n    t.assert.strictEqual(statusCode, 200)\n    t.assert.strictEqual(headers['content-type'], 'application/json')\n    t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, {\n      foo: 'bar'\n    })\n  })\n})\n\ntest('MockAgent - should not accept non-standard search parameters when acceptNonStandardSearchParameters is false (default)', async (t) => {\n  t.plan(2)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('(non-intercepted) response from server')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await once(server.listen(0), 'listening')\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET',\n    query: {\n      array: ['item1', 'item2']\n    }\n  }).reply(200, { foo: 'bar' }, {\n    headers: { 'content-type': 'application/json' },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, body } =\n  await mockAgent.request({\n    origin: baseUrl,\n    path: '/foo?array[]=item1&array[]=item2',\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const textResponse = await getResponse(body)\n  t.assert.strictEqual(textResponse, '(non-intercepted) response from server')\n})\n\n// https://github.com/nodejs/undici/issues/4703\ndescribe('MockAgent - case-insensitive origin matching', () => {\n  test('should match origins with different hostname case', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url1 = 'http://myEndpoint'\n    const url2 = 'http://myendpoint' // Different case\n\n    const mockPool = mockAgent.get(url1)\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET'\n      })\n      .reply(200, { success: true }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: url2, // Different case should still match\n      method: 'GET',\n      path: '/test'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { success: true })\n  })\n\n  test('should match URL object origin with string origin', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url = 'http://myEndpoint'\n\n    const mockPool = mockAgent.get(url)\n    mockPool\n      .intercept({\n        path: '/path',\n        method: 'GET'\n      })\n      .reply(200, { key: 'value' }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: new URL(url), // URL object should match string origin\n      method: 'GET',\n      path: '/path'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { key: 'value' })\n  })\n\n  test('should match URL object with different hostname case', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url1 = 'http://Example.com'\n    const url2 = new URL('http://example.com') // Different case\n\n    const mockPool = mockAgent.get(url1)\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET'\n      })\n      .reply(200, { success: true }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: url2, // URL object with different case should match\n      method: 'GET',\n      path: '/test'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { success: true })\n  })\n\n  test('should handle mixed case scenarios correctly', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url1 = 'http://MyEndpoint.com'\n    const url2 = 'http://myendpoint.com' // All lowercase\n\n    const mockPool = mockAgent.get(url1)\n    mockPool\n      .intercept({\n        path: '/api',\n        method: 'GET'\n      })\n      .reply(200, { data: 'test' }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: url2,\n      method: 'GET',\n      path: '/api'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { data: 'test' })\n  })\n\n  test('should preserve port numbers when normalizing', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url1 = 'http://Example.com:8080'\n    const url2 = 'http://example.com:8080' // Different case, same port\n\n    const mockPool = mockAgent.get(url1)\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET'\n      })\n      .reply(200, { port: 8080 }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: url2,\n      method: 'GET',\n      path: '/test'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { port: 8080 })\n  })\n\n  test('should handle https origins with case differences', async (t) => {\n    t.plan(2)\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const url1 = 'https://Api.Example.com'\n    const url2 = new URL('https://api.example.com') // Different case\n\n    const mockPool = mockAgent.get(url1)\n    mockPool\n      .intercept({\n        path: '/data',\n        method: 'GET'\n      })\n      .reply(200, { secure: true }, {\n        headers: { 'content-type': 'application/json' }\n      })\n\n    const { statusCode, body } = await mockAgent.request({\n      origin: url2,\n      method: 'GET',\n      path: '/data'\n    })\n\n    t.assert.strictEqual(statusCode, 200)\n    const jsonResponse = JSON.parse(await getResponse(body))\n    t.assert.deepStrictEqual(jsonResponse, { secure: true })\n  })\n})\n"
  },
  {
    "path": "test/mock-call-history-log.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { MockCallHistoryLog } = require('../lib/mock/mock-call-history')\nconst { InvalidArgumentError } = require('../lib/core/errors')\n\ndescribe('MockCallHistoryLog - constructor', () => {\n  function assertConsistent (t, mockCallHistoryLog) {\n    t.assert.strictEqual(mockCallHistoryLog.body, null)\n    t.assert.strictEqual(mockCallHistoryLog.headers, undefined)\n    t.assert.deepStrictEqual(mockCallHistoryLog.searchParams, { query: 'value' })\n    t.assert.strictEqual(mockCallHistoryLog.method, 'PUT')\n    t.assert.strictEqual(mockCallHistoryLog.origin, 'https://localhost:4000')\n    t.assert.strictEqual(mockCallHistoryLog.path, '/endpoint')\n    t.assert.strictEqual(mockCallHistoryLog.hash, '#here')\n    t.assert.strictEqual(mockCallHistoryLog.protocol, 'https:')\n    t.assert.strictEqual(mockCallHistoryLog.host, 'localhost:4000')\n    t.assert.strictEqual(mockCallHistoryLog.port, '4000')\n  }\n\n  test('should populate class properties with query in path', t => {\n    t.plan(10)\n\n    const mockCallHistoryLog = new MockCallHistoryLog({\n      body: null,\n      headers: undefined,\n      method: 'PUT',\n      origin: 'https://localhost:4000',\n      path: '/endpoint?query=value#here'\n    })\n\n    assertConsistent(t, mockCallHistoryLog)\n  })\n\n  test('should populate class properties with query in argument', t => {\n    t.plan(10)\n\n    const mockCallHistoryLog = new MockCallHistoryLog({\n      body: null,\n      headers: undefined,\n      method: 'PUT',\n      origin: 'https://localhost:4000',\n      path: '/endpoint#here',\n      query: { query: 'value' }\n    })\n\n    assertConsistent(t, mockCallHistoryLog)\n  })\n\n  test('should throw when url computing failed', t => {\n    t.plan(1)\n\n    t.assert.throws(() => new MockCallHistoryLog({}), new InvalidArgumentError('An error occurred when computing MockCallHistoryLog.url'))\n  })\n})\n\ndescribe('MockCallHistoryLog - toMap', () => {\n  test('should return a Map of eleven element', t => {\n    t.plan(1)\n\n    const mockCallHistoryLog = new MockCallHistoryLog({\n      body: '\"{}\"',\n      headers: { 'content-type': 'application/json' },\n      method: 'PUT',\n      origin: 'https://localhost:4000',\n      path: '/endpoint?query=value#here'\n    })\n\n    t.assert.strictEqual(mockCallHistoryLog.toMap().size, 11)\n  })\n})\n\ndescribe('MockCallHistoryLog - toString', () => {\n  test('should return a string with all property', t => {\n    t.plan(1)\n\n    const mockCallHistoryLog = new MockCallHistoryLog({\n      body: '\"{ \"data\": \"hello\" }\"',\n      headers: { 'content-type': 'application/json' },\n      method: 'PUT',\n      origin: 'https://localhost:4000',\n      path: '/endpoint?query=value#here'\n    })\n\n    t.assert.strictEqual(mockCallHistoryLog.toString(), 'protocol->https:|host->localhost:4000|port->4000|origin->https://localhost:4000|path->/endpoint|hash->#here|searchParams->{\"query\":\"value\"}|fullUrl->https://localhost:4000/endpoint?query=value#here|method->PUT|body->\"{ \"data\": \"hello\" }\"|headers->{\"content-type\":\"application/json\"}')\n  })\n\n  test('should return a string when headers is an Array of string Array', t => {\n    t.plan(1)\n\n    const mockCallHistoryLog = new MockCallHistoryLog({\n      body: '\"{ \"data\": \"hello\" }\"',\n      headers: ['content-type', ['application/json', 'application/xml']],\n      method: 'PUT',\n      origin: 'https://localhost:4000',\n      path: '/endpoint?query=value#here'\n    })\n\n    t.assert.strictEqual(mockCallHistoryLog.toString(), 'protocol->https:|host->localhost:4000|port->4000|origin->https://localhost:4000|path->/endpoint|hash->#here|searchParams->{\"query\":\"value\"}|fullUrl->https://localhost:4000/endpoint?query=value#here|method->PUT|body->\"{ \"data\": \"hello\" }\"|headers->[\"content-type\",[\"application/json\",\"application/xml\"]]')\n  })\n})\n"
  },
  {
    "path": "test/mock-call-history.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { MockCallHistory, MockCallHistoryLog } = require('../lib/mock/mock-call-history')\nconst { kMockCallHistoryAddLog } = require('../lib/mock/mock-symbols')\nconst { InvalidArgumentError } = require('../lib/core/errors')\n\ndescribe('MockCallHistory - constructor', () => {\n  test('should returns a MockCallHistory', t => {\n    t.plan(1)\n\n    const mockCallHistory = new MockCallHistory()\n\n    t.assert.ok(mockCallHistory instanceof MockCallHistory)\n  })\n})\n\ndescribe('MockCallHistory - add log', () => {\n  test('should add a log', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory()\n\n    t.assert.strictEqual(mockCallHistoryHello.calls().length, 0)\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'https://localhost:4000' })\n\n    t.assert.strictEqual(mockCallHistoryHello.calls().length, 1)\n  })\n})\n\ndescribe('MockCallHistory - calls', () => {\n  test('should returns every logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'https://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'https://localhost:4000' })\n\n    t.assert.strictEqual(mockCallHistoryHello.calls().length, 2)\n  })\n\n  test('should returns empty array when no logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.ok(mockCallHistoryHello.calls() instanceof Array)\n  })\n})\n\ndescribe('MockCallHistory - firstCall', () => {\n  test('should returns the first log registered', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    t.assert.strictEqual(mockCallHistoryHello.firstCall()?.path, '/')\n  })\n\n  test('should returns undefined when no logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.strictEqual(mockCallHistoryHello.firstCall(), undefined)\n  })\n})\n\ndescribe('MockCallHistory - lastCall', () => {\n  test('should returns the first log registered', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    t.assert.strictEqual(mockCallHistoryHello.lastCall()?.path, '/noop')\n  })\n\n  test('should returns undefined when no logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.strictEqual(mockCallHistoryHello.lastCall(), undefined)\n  })\n})\n\ndescribe('MockCallHistory - nthCall', () => {\n  test('should returns the nth log registered', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    t.assert.strictEqual(mockCallHistoryHello.nthCall(1)?.path, '/')\n    t.assert.strictEqual(mockCallHistoryHello.nthCall(2)?.path, '/noop')\n  })\n\n  test('should returns undefined when no logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.strictEqual(mockCallHistoryHello.nthCall(3), undefined)\n  })\n\n  test('should throw if index is not a number', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.throws(() => mockCallHistoryHello.nthCall('noop'), new InvalidArgumentError('nthCall must be called with a number'))\n  })\n\n  test('should throw if index is not an integer', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.throws(() => mockCallHistoryHello.nthCall(1.3), new InvalidArgumentError('nthCall must be called with an integer'))\n  })\n\n  test('should throw if index is equal to zero', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.throws(() => mockCallHistoryHello.nthCall(0), new InvalidArgumentError('nthCall must be called with a positive value. use firstCall or lastCall instead'))\n  })\n\n  test('should throw if index is negative', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    t.assert.throws(() => mockCallHistoryHello.nthCall(-1), new InvalidArgumentError('nthCall must be called with a positive value. use firstCall or lastCall instead'))\n  })\n})\n\ndescribe('MockCallHistory - iterator', () => {\n  test('should permit to iterate over logs with for..of', t => {\n    t.plan(4)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    for (const log of mockCallHistoryHello) {\n      t.assert.ok(log instanceof MockCallHistoryLog)\n      t.assert.ok(typeof log.path === 'string')\n    }\n  })\n\n  test('should permit to iterate over logs with spread operator', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    const logs = [...mockCallHistoryHello]\n\n    t.assert.ok(logs.every((log) => log instanceof MockCallHistoryLog))\n    t.assert.strictEqual(logs.length, 2)\n  })\n})\n\ndescribe('MockCallHistory - filterCalls without options', () => {\n  test('should filter logs with a function', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'http://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls((log) => log.path === '/noop')\n\n    t.assert.strictEqual(filtered?.[0]?.path, '/noop')\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should filter logs with a regexp', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls(/https:\\/\\//)\n\n    t.assert.strictEqual(filtered?.[0]?.path, '/noop')\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should filter logs with an object', t => {\n    t.plan(2)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ protocol: 'https:' })\n\n    t.assert.strictEqual(filtered?.[0]?.path, '/noop')\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should returns every logs with an empty object', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({})\n\n    t.assert.strictEqual(filtered.length, 3)\n  })\n\n  test('should filter logs with an object with host property', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://127.0.0.1:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ host: /localhost/ })\n\n    t.assert.strictEqual(filtered.length, 2)\n  })\n\n  test('should filter logs with an object with port property', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:1000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://127.0.0.1:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ port: '1000' })\n\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should filter logs with an object with hash property', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://127.0.0.1:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ hash: '#hello' })\n\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should filter logs with an object with fullUrl property', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '#hello', origin: 'http://localhost:1000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://127.0.0.1:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ fullUrl: 'http://localhost:1000/#hello' })\n\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should filter logs with an object with method property', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:1000', method: 'POST' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000', method: 'GET' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://127.0.0.1:4000', method: 'PUT' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ method: /(PUT|GET)/ })\n\n    t.assert.strictEqual(filtered.length, 2)\n  })\n\n  test('should use \"OR\" operator', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ protocol: 'https:', path: /^\\/$/ })\n\n    t.assert.strictEqual(filtered.length, 2)\n  })\n\n  test('should returns no duplicated logs', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/yes', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/noop', origin: 'https://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ protocol: 'https:', origin: /localhost/ })\n\n    t.assert.strictEqual(filtered.length, 3)\n  })\n\n  test('should throw if criteria is typeof number', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n\n    t.assert.throws(() => mockCallHistoryHello.filterCalls({ path: 3 }), new InvalidArgumentError('path parameter should be one of string, regexp, undefined or null'))\n  })\n\n  test('should throw if criteria is not a function, regexp, nor object', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n\n    t.assert.throws(() => mockCallHistoryHello.filterCalls(3), new InvalidArgumentError('criteria parameter should be one of function, regexp, or object'))\n  })\n})\n\ndescribe('MockCallHistory - filterCalls with options', () => {\n  test('should throw if options.operator is not a valid string', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n\n    t.assert.throws(() => mockCallHistoryHello.filterCalls({ path: '/' }, { operator: 'wrong' }), new InvalidArgumentError('options.operator must to be a case insensitive string equal to \\'OR\\' or \\'AND\\''))\n  })\n\n  test('should not throw if options.operator is \"or\"', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n\n    t.assert.doesNotThrow(() => mockCallHistoryHello.filterCalls({ path: '/' }, { operator: 'or' }))\n  })\n\n  test('should not throw if options.operator is \"and\"', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n\n    t.assert.doesNotThrow(() => mockCallHistoryHello.filterCalls({ path: '/' }, { operator: 'and' }))\n  })\n\n  test('should use \"OR\" operator if options is an empty object', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/foo', origin: 'http://localhost:4000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ path: '/' }, {})\n\n    t.assert.strictEqual(filtered.length, 1)\n  })\n\n  test('should use \"AND\" operator correctly', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/', origin: 'http://localhost:5000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/foo', origin: 'http://localhost:4000' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/foo', origin: 'http://localhost:5000' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ path: '/', port: '4000' }, { operator: 'AND' })\n\n    t.assert.strictEqual(filtered.length, 2)\n  })\n\n  test('should use \"AND\" operator with a lot of filters', t => {\n    t.plan(1)\n\n    const mockCallHistoryHello = new MockCallHistory('hello')\n\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000', method: 'GET' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000', method: 'GET' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000', method: 'DELETE' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000', method: 'POST' })\n    mockCallHistoryHello[kMockCallHistoryAddLog]({ path: '/#hello', origin: 'http://localhost:1000', method: 'PUT' })\n\n    const filtered = mockCallHistoryHello.filterCalls({ path: '/', port: '1000', host: /localhost/, method: /(POST|PUT)/ }, { operator: 'AND' })\n\n    t.assert.strictEqual(filtered.length, 2)\n  })\n})\n"
  },
  {
    "path": "test/mock-client.js",
    "content": "'use strict'\n\nconst { test, after, describe } = require('node:test')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { MockAgent, MockClient, setGlobalDispatcher, request } = require('..')\nconst { kUrl } = require('../lib/core/symbols')\nconst { kDispatches } = require('../lib/mock/mock-symbols')\nconst { InvalidArgumentError } = require('../lib/core/errors')\nconst { MockInterceptor } = require('../lib/mock/mock-interceptor')\nconst { getResponse } = require('../lib/mock/mock-utils')\nconst Dispatcher = require('../lib/dispatcher/dispatcher')\n\ndescribe('MockClient - constructor', () => {\n  test('fails if opts.agent does not implement `get` method', t => {\n    t.plan(1)\n    t.assert.throws(() => new MockClient('http://localhost:9999', { agent: { get: 'not a function' } }), InvalidArgumentError)\n  })\n\n  test('sets agent', t => {\n    t.plan(1)\n    t.assert.doesNotThrow(() => new MockClient('http://localhost:9999', { agent: new MockAgent({ connections: 1 }) }))\n  })\n\n  test('should implement the Dispatcher API', t => {\n    t.plan(1)\n\n    const mockClient = new MockClient('http://localhost:9999', { agent: new MockAgent({ connections: 1 }) })\n    t.assert.ok(mockClient instanceof Dispatcher)\n  })\n})\n\ndescribe('MockClient - dispatch', () => {\n  test('should handle a single interceptor', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get(baseUrl)\n\n    this[kUrl] = new URL('http://localhost:9999')\n    mockClient[kDispatches] = [\n      {\n        path: '/foo',\n        method: 'GET',\n        data: {\n          statusCode: 200,\n          data: 'hello',\n          headers: {},\n          trailers: {},\n          error: null\n        }\n      }\n    ]\n\n    t.assert.doesNotThrow(() => mockClient.dispatch({\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => resume(),\n      onData: () => {},\n      onComplete: () => {}\n    }))\n  })\n\n  test('should directly throw error from mockDispatch function if error is not a MockNotMatchedError', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get(baseUrl)\n\n    this[kUrl] = new URL('http://localhost:9999')\n    mockClient[kDispatches] = [\n      {\n        path: '/foo',\n        method: 'GET',\n        data: {\n          statusCode: 200,\n          data: 'hello',\n          headers: {},\n          trailers: {},\n          error: null\n        }\n      }\n    ]\n\n    t.assert.throws(() => mockClient.dispatch({\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => { throw new Error('kaboom') },\n      onData: () => {},\n      onComplete: () => {}\n    }), new Error('kaboom'))\n  })\n})\n\ntest('MockClient - intercept should return a MockInterceptor', (t) => {\n  t.plan(1)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n\n  const interceptor = mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  })\n\n  t.assert.ok(interceptor instanceof MockInterceptor)\n})\n\ndescribe('MockClient - intercept validation', () => {\n  test('it should error if no options specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get('http://localhost:9999')\n\n    t.assert.throws(() => mockClient.intercept(), new InvalidArgumentError('opts must be an object'))\n  })\n\n  test('it should error if no path specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get('http://localhost:9999')\n\n    t.assert.throws(() => mockClient.intercept({}), new InvalidArgumentError('opts.path must be defined'))\n  })\n\n  test('it should default to GET if no method specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent({ connections: 1 })\n    after(() => mockAgent.close())\n\n    const mockClient = mockAgent.get('http://localhost:9999')\n    t.assert.doesNotThrow(() => mockClient.intercept({ path: '/foo' }))\n  })\n\n  test('it should uppercase the method - https://github.com/nodejs/undici/issues/1320', t => {\n    t.plan(1)\n\n    const mockAgent = new MockAgent()\n    const mockClient = mockAgent.get('http://localhost:3000')\n\n    after(() => mockAgent.close())\n\n    mockClient.intercept({\n      path: '/test',\n      method: 'patch'\n    }).reply(200, 'Hello!')\n\n    t.assert.strictEqual(mockClient[kDispatches][0].method, 'PATCH')\n  })\n})\n\ntest('MockClient - close should run without error', async (t) => {\n  t.plan(1)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  mockClient[kDispatches] = [\n    {\n      path: '/foo',\n      method: 'GET',\n      data: {\n        statusCode: 200,\n        data: 'hello',\n        headers: {},\n        trailers: {},\n        error: null\n      }\n    }\n  ]\n\n  await mockClient.close()\n  t.assert.ok(true, 'pass')\n})\n\ntest('MockClient - should be able to set as globalDispatcher', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n  setGlobalDispatcher(mockClient)\n\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockClient - should support query params', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n  setGlobalDispatcher(mockClient)\n\n  const query = {\n    pageNum: 1\n  }\n  mockClient.intercept({\n    path: '/foo',\n    query,\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    query\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockClient - should intercept query params with hardcoded path', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n  setGlobalDispatcher(mockClient)\n\n  const query = {\n    pageNum: 1\n  }\n  mockClient.intercept({\n    path: '/foo?pageNum=1',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    query\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockClient - should intercept query params regardless of key ordering', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n  setGlobalDispatcher(mockClient)\n\n  const query = {\n    pageNum: 1,\n    limit: 100,\n    ordering: [false, true]\n  }\n\n  mockClient.intercept({\n    path: '/foo',\n    query: {\n      ordering: query.ordering,\n      pageNum: query.pageNum,\n      limit: query.limit\n    },\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    query\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockClient - should be able to use as a local dispatcher', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    dispatcher: mockClient\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockClient - basic intercept with MockClient.request', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n\n  mockClient.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: { 'content-type': 'application/json' },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await mockClient.request({\n    origin: baseUrl,\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\ntest('MockClient - cleans mocks', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent({ connections: 1 })\n  after(() => mockAgent.close())\n\n  const mockClient = mockAgent.get(baseUrl)\n  t.assert.ok(mockClient instanceof MockClient)\n  setGlobalDispatcher(mockClient)\n\n  mockClient.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(500, () => {\n    t.assert.fail('should not be called')\n  })\n\n  mockClient.cleanMocks()\n\n  t.assert.strictEqual(mockClient[kDispatches].length, 0)\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n"
  },
  {
    "path": "test/mock-delayed-abort.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { MockAgent, interceptors } = require('..')\nconst DecoratorHandler = require('../lib/handler/decorator-handler')\nconst { tspl } = require('@matteo.collina/tspl')\n\ntest('MockAgent with delayed response and AbortSignal should not cause uncaught errors', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const agent = new MockAgent()\n  t.after(() => agent.close())\n\n  const mockPool = agent.get('https://example.com')\n  mockPool.intercept({ path: '/test', method: 'GET' })\n    .reply(200, { success: true }, { headers: { 'content-type': 'application/json' } })\n    .delay(100)\n\n  const ac = new AbortController()\n\n  // Abort the request after 10ms\n  setTimeout(() => {\n    ac.abort(new Error('Request aborted'))\n  }, 10)\n\n  try {\n    await agent.request({\n      origin: 'https://example.com',\n      path: '/test',\n      method: 'GET',\n      signal: ac.signal\n    })\n    p.fail('Should have thrown an error')\n  } catch (err) {\n    p.ok(err.message === 'Request aborted' || err.name === 'AbortError', 'Error should be related to abort')\n  }\n\n  // Wait for the delayed response to fire - should not cause any uncaught errors\n  await new Promise(resolve => setTimeout(resolve, 150))\n\n  p.ok(true, 'No uncaught errors after delayed response')\n})\n\ntest('MockAgent with delayed response and composed interceptor (decompress) should not cause uncaught errors', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  // The decompress interceptor has assertions that fail if onResponseStart is called after onError\n  const agent = new MockAgent().compose(interceptors.decompress())\n  t.after(() => agent.close())\n\n  const mockPool = agent.get('https://example.com')\n  mockPool.intercept({ path: '/test', method: 'GET' })\n    .reply(200, { success: true }, { headers: { 'content-type': 'application/json' } })\n    .delay(100)\n\n  const ac = new AbortController()\n\n  // Abort the request after 10ms\n  setTimeout(() => {\n    ac.abort(new Error('Request aborted'))\n  }, 10)\n\n  try {\n    await agent.request({\n      origin: 'https://example.com',\n      path: '/test',\n      method: 'GET',\n      signal: ac.signal\n    })\n    p.fail('Should have thrown an error')\n  } catch (err) {\n    p.ok(err.message === 'Request aborted' || err.name === 'AbortError', 'Error should be related to abort')\n  }\n\n  // Wait for the delayed response to fire - should not cause any uncaught errors\n  await new Promise(resolve => setTimeout(resolve, 150))\n\n  p.ok(true, 'No uncaught errors after delayed response')\n})\n\ntest('MockAgent with delayed response and DecoratorHandler should not call onResponseStart after onError', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  class TestDecoratorHandler extends DecoratorHandler {\n    #onErrorCalled = false\n\n    onResponseStart (controller, statusCode, headers, statusMessage) {\n      if (this.#onErrorCalled) {\n        p.fail('onResponseStart should not be called after onError')\n      }\n      return super.onResponseStart(controller, statusCode, headers, statusMessage)\n    }\n\n    onResponseError (controller, err) {\n      this.#onErrorCalled = true\n      return super.onResponseError(controller, err)\n    }\n  }\n\n  const agent = new MockAgent()\n  t.after(() => agent.close())\n\n  const mockPool = agent.get('https://example.com')\n  mockPool.intercept({ path: '/test', method: 'GET' })\n    .reply(200, { success: true }, { headers: { 'content-type': 'application/json' } })\n    .delay(100)\n\n  const ac = new AbortController()\n\n  // Abort the request after 10ms\n  setTimeout(() => {\n    ac.abort(new Error('Request aborted'))\n  }, 10)\n\n  const originalDispatch = agent.dispatch.bind(agent)\n  agent.dispatch = (opts, handler) => {\n    const decoratedHandler = new TestDecoratorHandler(handler)\n    return originalDispatch(opts, decoratedHandler)\n  }\n\n  try {\n    await agent.request({\n      origin: 'https://example.com',\n      path: '/test',\n      method: 'GET',\n      signal: ac.signal\n    })\n    p.fail('Should have thrown an error')\n  } catch (err) {\n    p.ok(err.message === 'Request aborted' || err.name === 'AbortError', 'Error should be related to abort')\n  }\n\n  // Wait for the delayed response to fire\n  await new Promise(resolve => setTimeout(resolve, 150))\n\n  p.ok(true, 'Decorator handler invariants maintained')\n})\n"
  },
  {
    "path": "test/mock-errors.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst { mockErrors, errors } = require('..')\n\ndescribe('MockNotMatchedError', () => {\n  test('should implement an UndiciError', t => {\n    t.plan(4)\n\n    const mockError = new mockErrors.MockNotMatchedError()\n    t.assert.ok(mockError instanceof errors.UndiciError)\n    t.assert.deepStrictEqual(mockError.name, 'MockNotMatchedError')\n    t.assert.deepStrictEqual(mockError.code, 'UND_MOCK_ERR_MOCK_NOT_MATCHED')\n    t.assert.deepStrictEqual(mockError.message, 'The request does not match any registered mock dispatches')\n  })\n\n  test('should set a custom message', t => {\n    t.plan(4)\n\n    const mockError = new mockErrors.MockNotMatchedError('custom message')\n    t.assert.ok(mockError instanceof errors.UndiciError)\n    t.assert.deepStrictEqual(mockError.name, 'MockNotMatchedError')\n    t.assert.deepStrictEqual(mockError.code, 'UND_MOCK_ERR_MOCK_NOT_MATCHED')\n    t.assert.deepStrictEqual(mockError.message, 'custom message')\n  })\n})\n"
  },
  {
    "path": "test/mock-interceptor-unused-assertions.js",
    "content": "'use strict'\n\nconst { test, beforeEach, afterEach } = require('node:test')\nconst { MockAgent, setGlobalDispatcher } = require('..')\nconst PendingInterceptorsFormatter = require('../lib/mock/pending-interceptors-formatter')\nconst util = require('../lib/core/util')\n\n// Since Node.js v21 `console.table` rows are aligned to the left\n// https://github.com/nodejs/node/pull/50135\nconst tableRowsAlignedToLeft = util.nodeMajor >= 21 || (util.nodeMajor === 20 && util.nodeMinor >= 11)\n\n// `console.table` treats emoji as two character widths for cell width determination\nconst Y = process.versions.icu ? '✅' : 'Y '\nconst N = process.versions.icu ? '❌' : 'N '\n\n// Avoid colors in the output for inline snapshots.\nconst pendingInterceptorsFormatter = new PendingInterceptorsFormatter({ disableColors: true })\n\nlet originalGlobalDispatcher\n\nconst origin = 'https://localhost:9999'\n\nbeforeEach(() => {\n  // Disallow all network activity by default by using a mock agent as the global dispatcher\n  const globalDispatcher = new MockAgent()\n  globalDispatcher.disableNetConnect()\n  setGlobalDispatcher(globalDispatcher)\n  originalGlobalDispatcher = globalDispatcher\n})\n\nafterEach(() => {\n  setGlobalDispatcher(originalGlobalDispatcher)\n})\n\nfunction mockAgentWithOneInterceptor () {\n  const agent = new MockAgent()\n  agent.disableNetConnect()\n\n  agent\n    .get('https://example.com')\n    .intercept({ method: 'GET', path: '/' })\n    .reply(200, '')\n\n  return agent\n}\n\ntest('1 pending interceptor', t => {\n  t.plan(1)\n\n  try {\n    mockAgentWithOneInterceptor().assertNoPendingInterceptors({ pendingInterceptorsFormatter })\n    t.assert.fail('Should have thrown')\n  } catch (err) {\n    t.assert.deepStrictEqual(err.message, tableRowsAlignedToLeft\n      ? `\n1 interceptor is pending:\n\n┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │ Origin                │ Path │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤\n│ 0       │ 'GET'  │ 'https://example.com' │ '/'  │ 200         │ '${N}'       │ 0           │ 1         │\n└─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim()\n      : `\n1 interceptor is pending:\n\n┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │        Origin         │ Path │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤\n│    0    │ 'GET'  │ 'https://example.com' │ '/'  │     200     │    '${N}'    │      0      │     1     │\n└─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim())\n  }\n})\n\ntest('2 pending interceptors', t => {\n  t.plan(1)\n\n  const withTwoInterceptors = mockAgentWithOneInterceptor()\n  withTwoInterceptors\n    .get(origin)\n    .intercept({ method: 'get', path: '/some/path' })\n    .reply(204, 'OK')\n  try {\n    withTwoInterceptors.assertNoPendingInterceptors({ pendingInterceptorsFormatter })\n  } catch (err) {\n    t.assert.deepStrictEqual(err.message, tableRowsAlignedToLeft\n      ? `\n2 interceptors are pending:\n\n┌─────────┬────────┬──────────────────────────┬──────────────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │ Origin                   │ Path         │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼──────────────────────────┼──────────────┼─────────────┼────────────┼─────────────┼───────────┤\n│ 0       │ 'GET'  │ 'https://example.com'    │ '/'          │ 200         │ '${N}'       │ 0           │ 1         │\n│ 1       │ 'GET'  │ 'https://localhost:9999' │ '/some/path' │ 204         │ '${N}'       │ 0           │ 1         │\n└─────────┴────────┴──────────────────────────┴──────────────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim()\n      : `\n2 interceptors are pending:\n\n┌─────────┬────────┬──────────────────────────┬──────────────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │          Origin          │     Path     │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼──────────────────────────┼──────────────┼─────────────┼────────────┼─────────────┼───────────┤\n│    0    │ 'GET'  │  'https://example.com'   │     '/'      │     200     │    '${N}'    │      0      │     1     │\n│    1    │ 'GET'  │ 'https://localhost:9999' │ '/some/path' │     204     │    '${N}'    │      0      │     1     │\n└─────────┴────────┴──────────────────────────┴──────────────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim())\n  }\n})\n\ntest('Variations of persist(), times(), and pending status', async t => {\n  t.plan(6)\n\n  // Agent with unused interceptor\n  const agent = mockAgentWithOneInterceptor()\n\n  // Unused with persist()\n  agent\n    .get(origin)\n    .intercept({ method: 'get', path: '/persistent/unused' })\n    .reply(200, 'OK')\n    .persist()\n\n  // Used with persist()\n  agent\n    .get(origin)\n    .intercept({ method: 'GET', path: '/persistent/used' })\n    .reply(200, 'OK')\n    .persist()\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/persistent/used' })).statusCode, 200)\n\n  // Consumed without persist()\n  agent.get(origin)\n    .intercept({ method: 'post', path: '/transient/pending' })\n    .reply(201, 'Created')\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'POST', path: '/transient/pending' })).statusCode, 201)\n\n  // Partially pending with times()\n  agent.get(origin)\n    .intercept({ method: 'get', path: '/times/partial' })\n    .reply(200, 'OK')\n    .times(5)\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/times/partial' })).statusCode, 200)\n\n  // Unused with times()\n  agent.get(origin)\n    .intercept({ method: 'get', path: '/times/unused' })\n    .reply(200, 'OK')\n    .times(2)\n\n  // Fully pending with times()\n  agent.get(origin)\n    .intercept({ method: 'get', path: '/times/pending' })\n    .reply(200, 'OK')\n    .times(2)\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/times/pending' })).statusCode, 200)\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/times/pending' })).statusCode, 200)\n\n  try {\n    agent.assertNoPendingInterceptors({ pendingInterceptorsFormatter })\n    t.assert.fail('Should have thrown')\n  } catch (err) {\n    t.assert.deepStrictEqual(err.message, tableRowsAlignedToLeft\n      ? `\n4 interceptors are pending:\n\n┌─────────┬────────┬──────────────────────────┬──────────────────────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │ Origin                   │ Path                 │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼──────────────────────────┼──────────────────────┼─────────────┼────────────┼─────────────┼───────────┤\n│ 0       │ 'GET'  │ 'https://example.com'    │ '/'                  │ 200         │ '${N}'       │ 0           │ 1         │\n│ 1       │ 'GET'  │ 'https://localhost:9999' │ '/persistent/unused' │ 200         │ '${Y}'       │ 0           │ Infinity  │\n│ 2       │ 'GET'  │ 'https://localhost:9999' │ '/times/partial'     │ 200         │ '${N}'       │ 1           │ 4         │\n│ 3       │ 'GET'  │ 'https://localhost:9999' │ '/times/unused'      │ 200         │ '${N}'       │ 0           │ 2         │\n└─────────┴────────┴──────────────────────────┴──────────────────────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim()\n      : `\n4 interceptors are pending:\n\n┌─────────┬────────┬──────────────────────────┬──────────────────────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │          Origin          │         Path         │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼──────────────────────────┼──────────────────────┼─────────────┼────────────┼─────────────┼───────────┤\n│    0    │ 'GET'  │  'https://example.com'   │         '/'          │     200     │    '${N}'    │      0      │     1     │\n│    1    │ 'GET'  │ 'https://localhost:9999' │ '/persistent/unused' │     200     │    '${Y}'    │      0      │ Infinity  │\n│    2    │ 'GET'  │ 'https://localhost:9999' │   '/times/partial'   │     200     │    '${N}'    │      1      │     4     │\n│    3    │ 'GET'  │ 'https://localhost:9999' │   '/times/unused'    │     200     │    '${N}'    │      0      │     2     │\n└─────────┴────────┴──────────────────────────┴──────────────────────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim())\n  }\n})\n\ntest('works when no interceptors are registered', t => {\n  t.plan(2)\n\n  const agent = new MockAgent()\n  agent.disableNetConnect()\n\n  t.assert.deepStrictEqual(agent.pendingInterceptors(), [])\n  t.assert.doesNotThrow(() => agent.assertNoPendingInterceptors())\n})\n\ntest('works when all interceptors are pending', async t => {\n  t.plan(4)\n\n  const agent = new MockAgent()\n  agent.disableNetConnect()\n\n  agent.get(origin).intercept({ method: 'get', path: '/' }).reply(200, 'OK')\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/' })).statusCode, 200)\n\n  agent.get(origin).intercept({ method: 'get', path: '/persistent' }).reply(200, 'OK')\n  t.assert.deepStrictEqual((await agent.request({ origin, method: 'GET', path: '/persistent' })).statusCode, 200)\n\n  t.assert.deepStrictEqual(agent.pendingInterceptors(), [])\n  t.assert.doesNotThrow(() => agent.assertNoPendingInterceptors())\n})\n\ntest('defaults to rendering output with terminal color when process.env.CI is unset', t => {\n  t.plan(1)\n\n  // This ensures that the test works in an environment where the CI env var is set.\n  const oldCiEnvVar = process.env.CI\n  delete process.env.CI\n\n  try {\n    mockAgentWithOneInterceptor().assertNoPendingInterceptors()\n    t.assert.fail('Should have thrown')\n  } catch (err) {\n    t.assert.deepStrictEqual(err.message, tableRowsAlignedToLeft\n      ? `\n1 interceptor is pending:\n\n┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │ Origin                │ Path │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤\n│ 0       │ \\u001b[32m'GET'\\u001b[39m  │ \\u001b[32m'https://example.com'\\u001b[39m │ \\u001b[32m'/'\\u001b[39m  │ \\u001b[33m200\\u001b[39m         │ \\u001b[32m'${N}'\\u001b[39m       │ \\u001b[33m0\\u001b[39m           │ \\u001b[33m1\\u001b[39m         │\n└─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim()\n      : `\n1 interceptor is pending:\n\n┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐\n│ (index) │ Method │        Origin         │ Path │ Status code │ Persistent │ Invocations │ Remaining │\n├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤\n│    0    │ \\u001b[32m'GET'\\u001b[39m  │ \\u001b[32m'https://example.com'\\u001b[39m │ \\u001b[32m'/'\\u001b[39m  │     \\u001b[33m200\\u001b[39m     │    \\u001b[32m'${N}'\\u001b[39m    │      \\u001b[33m0\\u001b[39m      │     \\u001b[33m1\\u001b[39m     │\n└─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘\n`.trim())\n\n    // Re-set the CI env var if it were set.\n    // Assigning `undefined` does not work,\n    // because reading the env var afterwards yields the string 'undefined',\n    // so we need to re-set it conditionally.\n    if (oldCiEnvVar != null) {\n      process.env.CI = oldCiEnvVar\n    }\n  }\n})\n\ntest('returns unused interceptors', t => {\n  t.plan(1)\n\n  t.assert.deepStrictEqual(mockAgentWithOneInterceptor().pendingInterceptors(), [\n    {\n      timesInvoked: 0,\n      times: 1,\n      persist: false,\n      consumed: false,\n      pending: true,\n      ignoreTrailingSlash: false,\n      path: '/',\n      method: 'GET',\n      body: undefined,\n      query: undefined,\n      headers: undefined,\n      data: {\n        error: null,\n        statusCode: 200,\n        data: '',\n        headers: {},\n        trailers: {}\n      },\n      origin: 'https://example.com'\n    }\n  ])\n})\n"
  },
  {
    "path": "test/mock-interceptor.js",
    "content": "'use strict'\n\nconst { describe, test, after } = require('node:test')\nconst { MockInterceptor, MockScope } = require('../lib/mock/mock-interceptor')\nconst MockAgent = require('../lib/mock/mock-agent')\nconst { kDispatchKey } = require('../lib/mock/mock-symbols')\nconst { InvalidArgumentError } = require('../lib/core/errors')\nconst { fetch } = require('../lib/web/fetch/index')\n\ndescribe('MockInterceptor - path', () => {\n  test('should remove hash fragment from paths', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '#foobar',\n      method: ''\n    }, [])\n    t.assert.strictEqual(mockInterceptor[kDispatchKey].path, '')\n  })\n})\n\ndescribe('MockInterceptor - reply', () => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.reply(200, 'hello')\n    t.assert.ok(result instanceof MockScope)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(2)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockInterceptor.reply(), new InvalidArgumentError('statusCode must be defined'))\n    t.assert.throws(() => mockInterceptor.reply(200, '', 'hello'), new InvalidArgumentError('responseOptions must be an object'))\n  })\n})\n\ndescribe('MockInterceptor - reply callback', () => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.reply(200, () => 'hello')\n    t.assert.ok(result instanceof MockScope)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(3)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockInterceptor.reply(), new InvalidArgumentError('statusCode must be defined'))\n    t.assert.throws(() => mockInterceptor.reply(200, () => { }, 'hello'), new InvalidArgumentError('responseOptions must be an object'))\n    t.assert.throws(() => mockInterceptor.reply(200, () => { }, null), new InvalidArgumentError('responseOptions must be an object'))\n  })\n})\n\ndescribe('MockInterceptor - reply options callback', () => {\n  test('should return MockScope', t => {\n    t.plan(2)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.reply((options) => ({\n      statusCode: 200,\n      data: 'hello'\n    }))\n    t.assert.ok(result instanceof MockScope)\n\n    // Test parameters\n\n    const baseUrl = 'http://localhost:9999'\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    mockPool.intercept({\n      path: '/test',\n      method: 'GET'\n    }).reply((options) => {\n      t.assert.deepStrictEqual(options, { path: '/test', method: 'GET', headers: { foo: 'bar' } })\n      return { statusCode: 200, data: 'hello' }\n    })\n\n    mockPool.dispatch({\n      path: '/test',\n      method: 'GET',\n      headers: { foo: 'bar' }\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    })\n  })\n\n  test('should handle undefined data', t => {\n    t.plan(2)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.reply((options) => ({\n      statusCode: 200,\n      data: undefined\n    }))\n    t.assert.ok(result instanceof MockScope)\n\n    // Test parameters\n\n    const baseUrl = 'http://localhost:9999'\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    mockPool.intercept({\n      path: '/test',\n      method: 'GET'\n    }).reply((options) => {\n      t.assert.deepStrictEqual(options, { path: '/test', method: 'GET', headers: { foo: 'bar' } })\n      return { statusCode: 200, data: 'hello' }\n    })\n\n    mockPool.dispatch({\n      path: '/test',\n      method: 'GET',\n      headers: { foo: 'bar' }\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    })\n  })\n\n  test('should error if passed options invalid', async (t) => {\n    t.plan(4)\n\n    const baseUrl = 'http://localhost:9999'\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    mockPool.intercept({\n      path: '/test-return-undefined',\n      method: 'GET'\n    }).reply(() => { })\n\n    mockPool.intercept({\n      path: '/test-return-null',\n      method: 'GET'\n    }).reply(() => { return null })\n\n    mockPool.intercept({\n      path: '/test3',\n      method: 'GET'\n    }).reply(() => ({\n      statusCode: 200,\n      data: 'hello',\n      responseOptions: 42\n    }))\n\n    mockPool.intercept({\n      path: '/test4',\n      method: 'GET'\n    }).reply(() => ({\n      data: 'hello',\n      responseOptions: 42\n    }))\n\n    t.assert.throws(() => mockPool.dispatch({\n      path: '/test-return-undefined',\n      method: 'GET'\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    }), new InvalidArgumentError('reply options callback must return an object'))\n\n    t.assert.throws(() => mockPool.dispatch({\n      path: '/test-return-null',\n      method: 'GET'\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    }), new InvalidArgumentError('reply options callback must return an object'))\n\n    t.assert.throws(() => mockPool.dispatch({\n      path: '/test3',\n      method: 'GET'\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    }), new InvalidArgumentError('responseOptions must be an object'))\n\n    t.assert.throws(() => mockPool.dispatch({\n      path: '/test4',\n      method: 'GET'\n    }, {\n      onHeaders: () => { },\n      onData: () => { },\n      onComplete: () => { }\n    }), new InvalidArgumentError('statusCode must be defined'))\n  })\n})\n\ndescribe('MockInterceptor - replyWithError', () => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.replyWithError(new Error('kaboom'))\n    t.assert.ok(result instanceof MockScope)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(1)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockInterceptor.replyWithError(), new InvalidArgumentError('error must be defined'))\n  })\n})\n\ndescribe('MockInterceptor - defaultReplyHeaders', () => {\n  test('should return MockInterceptor', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.defaultReplyHeaders({})\n    t.assert.ok(result instanceof MockInterceptor)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(1)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockInterceptor.defaultReplyHeaders(), new InvalidArgumentError('headers must be defined'))\n  })\n})\n\ndescribe('MockInterceptor - defaultReplyTrailers', () => {\n  test('should return MockInterceptor', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.defaultReplyTrailers({})\n    t.assert.ok(result instanceof MockInterceptor)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(1)\n\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockInterceptor.defaultReplyTrailers(), new InvalidArgumentError('trailers must be defined'))\n  })\n})\n\ndescribe('MockInterceptor - replyContentLength', () => {\n  test('should return MockInterceptor', t => {\n    t.plan(1)\n    const mockInterceptor = new MockInterceptor({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockInterceptor.defaultReplyTrailers({})\n    t.assert.ok(result instanceof MockInterceptor)\n  })\n})\n\ndescribe('https://github.com/nodejs/undici/issues/3649', () => {\n  [\n    ['/api/some-path', '/api/some-path'],\n    ['/api/some-path/', '/api/some-path'],\n    ['/api/some-path', '/api/some-path/'],\n    ['/api/some-path/', '/api/some-path/'],\n    ['/api/some-path////', '/api/some-path//'],\n    ['', ''],\n    ['/', ''],\n    ['', '/'],\n    ['/', '/']\n  ].forEach(([interceptPath, fetchedPath], index) => {\n    test(`MockAgent should match with or without trailing slash by setting ignoreTrailingSlash as MockAgent option /${index}`, async (t) => {\n      t.plan(1)\n\n      const mockAgent = new MockAgent({ ignoreTrailingSlash: true })\n      mockAgent.disableNetConnect()\n      mockAgent\n        .get('https://localhost')\n        .intercept({ path: interceptPath }).reply(200, { ok: true })\n\n      const res = await fetch(new URL(fetchedPath, 'https://localhost'), { dispatcher: mockAgent })\n\n      t.assert.deepStrictEqual(await res.json(), { ok: true })\n    })\n\n    test(`MockAgent should match with or without trailing slash by setting ignoreTrailingSlash as intercept option /${index}`, async (t) => {\n      t.plan(1)\n\n      const mockAgent = new MockAgent()\n      mockAgent.disableNetConnect()\n      mockAgent\n        .get('https://localhost')\n        .intercept({ path: interceptPath, ignoreTrailingSlash: true }).reply(200, { ok: true })\n\n      const res = await fetch(new URL(fetchedPath, 'https://localhost'), { dispatcher: mockAgent })\n\n      t.assert.deepStrictEqual(await res.json(), { ok: true })\n    })\n\n    if (\n      (interceptPath === fetchedPath && (interceptPath !== '' && fetchedPath !== '')) ||\n      (interceptPath === '/' && fetchedPath === '')\n    ) {\n      test(`MockAgent should should match on strict equal cases of paths when ignoreTrailingSlash is not set /${index}`, async (t) => {\n        t.plan(1)\n\n        const mockAgent = new MockAgent()\n        mockAgent.disableNetConnect()\n        mockAgent\n          .get('https://localhost')\n          .intercept({ path: interceptPath }).reply(200, { ok: true })\n\n        const res = await fetch(new URL(fetchedPath, 'https://localhost'), { dispatcher: mockAgent })\n\n        t.assert.deepStrictEqual(await res.json(), { ok: true })\n      })\n    } else {\n      test(`MockAgent should should reject on not strict equal cases of paths when ignoreTrailingSlash is not set /${index}`, async (t) => {\n        t.plan(1)\n\n        const mockAgent = new MockAgent()\n        mockAgent.disableNetConnect()\n        mockAgent\n          .get('https://localhost')\n          .intercept({ path: interceptPath }).reply(200, { ok: true })\n\n        await t.assert.rejects(fetch(new URL(fetchedPath, 'https://localhost'), { dispatcher: mockAgent }))\n      })\n    }\n  })\n})\n\ndescribe('MockInterceptor - different payloads', () => {\n  [\n    // Buffer\n    ['arrayBuffer', 'ArrayBuffer', 'ArrayBuffer', new TextEncoder().encode('{\"test\":true}').buffer, new TextEncoder().encode('{\"test\":true}').buffer],\n    ['json', 'ArrayBuffer', 'Object', new TextEncoder().encode('{\"test\":true}').buffer, { test: true }],\n    ['bytes', 'ArrayBuffer', 'Uint8Array', new TextEncoder().encode('{\"test\":true}').buffer, new TextEncoder().encode('{\"test\":true}')],\n    ['text', 'ArrayBuffer', 'string', new TextEncoder().encode('{\"test\":true}').buffer, '{\"test\":true}'],\n\n    // Buffer\n    ['arrayBuffer', 'Buffer', 'ArrayBuffer', Buffer.from('{\"test\":true}'), new TextEncoder().encode('{\"test\":true}').buffer],\n    ['json', 'Buffer', 'Object', Buffer.from('{\"test\":true}'), { test: true }],\n    ['bytes', 'Buffer', 'Uint8Array', Buffer.from('{\"test\":true}'), new TextEncoder().encode('{\"test\":true}')],\n    ['text', 'Buffer', 'string', Buffer.from('{\"test\":true}'), '{\"test\":true}'],\n\n    // Uint8Array\n    ['arrayBuffer', 'Uint8Array', 'ArrayBuffer', new TextEncoder().encode('{\"test\":true}'), new TextEncoder().encode('{\"test\":true}').buffer],\n    ['json', 'Uint8Array', 'Object', new TextEncoder().encode('{\"test\":true}'), { test: true }],\n    ['bytes', 'Uint8Array', 'Uint8Array', new TextEncoder().encode('{\"test\":true}'), new TextEncoder().encode('{\"test\":true}')],\n    ['text', 'Uint8Array', 'string', new TextEncoder().encode('{\"test\":true}'), '{\"test\":true}'],\n\n    // string\n    ['arrayBuffer', 'string', 'ArrayBuffer', '{\"test\":true}', new TextEncoder().encode('{\"test\":true}').buffer],\n    ['json', 'string', 'Object', '{\"test\":true}', { test: true }],\n    ['bytes', 'string', 'Uint8Array', '{\"test\":true}', new TextEncoder().encode('{\"test\":true}')],\n    ['text', 'string', 'string', '{\"test\":true}', '{\"test\":true}'],\n\n    // object\n    ['arrayBuffer', 'Object', 'ArrayBuffer', { test: true }, new TextEncoder().encode('{\"test\":true}').buffer],\n    ['json', 'Object', 'Object', { test: true }, { test: true }],\n    ['bytes', 'Object', 'Uint8Array', { test: true }, new TextEncoder().encode('{\"test\":true}')],\n    ['text', 'Object', 'string', { test: true }, '{\"test\":true}']\n  ].forEach(([method, inputType, outputType, input, output]) => {\n    test(`${inputType} will be returned as ${outputType} via ${method}()`, async (t) => {\n      t.plan(1)\n\n      const mockAgent = new MockAgent()\n      mockAgent.disableNetConnect()\n      mockAgent\n        .get('https://localhost')\n        .intercept({ path: '/' }).reply(200, input)\n\n      const response = await fetch('https://localhost', { dispatcher: mockAgent })\n\n      t.assert.deepStrictEqual(await response[method](), output)\n    })\n  })\n})\n"
  },
  {
    "path": "test/mock-pool.js",
    "content": "'use strict'\n\nconst { test, after, describe } = require('node:test')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { MockAgent, MockPool, getGlobalDispatcher, setGlobalDispatcher, request } = require('..')\nconst { kUrl } = require('../lib/core/symbols')\nconst { kDispatches } = require('../lib/mock/mock-symbols')\nconst { InvalidArgumentError } = require('../lib/core/errors')\nconst { MockInterceptor } = require('../lib/mock/mock-interceptor')\nconst { getResponse } = require('../lib/mock/mock-utils')\nconst Dispatcher = require('../lib/dispatcher/dispatcher')\nconst { fetch } = require('..')\n\ndescribe('MockPool - constructor', () => {\n  test('fails if opts.agent does not implement `get` method', t => {\n    t.plan(1)\n    t.assert.throws(() => new MockPool('http://localhost:9999', { agent: { get: 'not a function' } }), InvalidArgumentError)\n  })\n\n  test('sets agent', t => {\n    t.plan(1)\n    t.assert.doesNotThrow(() => new MockPool('http://localhost:9999', { agent: new MockAgent() }))\n  })\n\n  test('should implement the Dispatcher API', t => {\n    t.plan(1)\n\n    const mockPool = new MockPool('http://localhost:9999', { agent: new MockAgent() })\n    t.assert.ok(mockPool instanceof Dispatcher)\n  })\n})\n\ndescribe('MockPool - dispatch', () => {\n  test('should handle a single interceptor', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    this[kUrl] = new URL('http://localhost:9999')\n    mockPool[kDispatches] = [\n      {\n        path: '/foo',\n        method: 'GET',\n        data: {\n          statusCode: 200,\n          data: 'hello',\n          headers: {},\n          trailers: {},\n          error: null\n        }\n      }\n    ]\n\n    t.assert.doesNotThrow(() => mockPool.dispatch({\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => resume(),\n      onData: () => { },\n      onComplete: () => { }\n    }))\n  })\n\n  test('should directly throw error from mockDispatch function if error is not a MockNotMatchedError', (t) => {\n    t.plan(1)\n\n    const baseUrl = 'http://localhost:9999'\n\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get(baseUrl)\n\n    this[kUrl] = new URL('http://localhost:9999')\n    mockPool[kDispatches] = [\n      {\n        path: '/foo',\n        method: 'GET',\n        data: {\n          statusCode: 200,\n          data: 'hello',\n          headers: {},\n          trailers: {},\n          error: null\n        }\n      }\n    ]\n\n    t.assert.throws(() => mockPool.dispatch({\n      path: '/foo',\n      method: 'GET'\n    }, {\n      onHeaders: (_statusCode, _headers, resume) => { throw new Error('kaboom') },\n      onData: () => { },\n      onComplete: () => { }\n    }), new Error('kaboom'))\n  })\n})\n\ntest('MockPool - intercept should return a MockInterceptor', (t) => {\n  t.plan(1)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n\n  const interceptor = mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  })\n\n  t.assert.ok(interceptor instanceof MockInterceptor)\n})\n\ndescribe('MockPool - intercept validation', () => {\n  test('it should error if no options specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get('http://localhost:9999')\n\n    t.assert.throws(() => mockPool.intercept(), new InvalidArgumentError('opts must be an object'))\n  })\n\n  test('it should error if no path specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get('http://localhost:9999')\n\n    t.assert.throws(() => mockPool.intercept({}), new InvalidArgumentError('opts.path must be defined'))\n  })\n\n  test('it should default to GET if no method specified in the intercept', t => {\n    t.plan(1)\n    const mockAgent = new MockAgent()\n    after(() => mockAgent.close())\n\n    const mockPool = mockAgent.get('http://localhost:9999')\n    t.assert.doesNotThrow(() => mockPool.intercept({ path: '/foo' }))\n  })\n})\n\ntest('MockPool - close should run without error', async (t) => {\n  t.plan(1)\n\n  const baseUrl = 'http://localhost:9999'\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n\n  mockPool[kDispatches] = [\n    {\n      path: '/foo',\n      method: 'GET',\n      data: {\n        statusCode: 200,\n        data: 'hello',\n        headers: {},\n        trailers: {},\n        error: null\n      }\n    }\n  ]\n\n  await mockPool.close()\n  t.assert.ok(true, 'pass')\n})\n\ntest('MockPool - should be able to set as globalDispatcher', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  t.assert.ok(mockPool instanceof MockPool)\n  setGlobalDispatcher(mockPool)\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockPool - should be able to use as a local dispatcher', async (t) => {\n  t.plan(3)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  t.assert.ok(mockPool instanceof MockPool)\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(200, 'hello')\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET',\n    dispatcher: mockPool\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n\ntest('MockPool - basic intercept with MockPool.request', async (t) => {\n  t.plan(5)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('should not be called')\n    t.assert.fail('should not be called')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n  const mockPool = mockAgent.get(baseUrl)\n  t.assert.ok(mockPool instanceof MockPool)\n\n  mockPool.intercept({\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  }).reply(200, { foo: 'bar' }, {\n    headers: { 'content-type': 'application/json' },\n    trailers: { 'Content-MD5': 'test' }\n  })\n\n  const { statusCode, headers, trailers, body } = await mockPool.request({\n    origin: baseUrl,\n    path: '/foo?hello=there&see=ya',\n    method: 'POST',\n    body: 'form1=data1&form2=data2'\n  })\n  t.assert.strictEqual(statusCode, 200)\n  t.assert.strictEqual(headers['content-type'], 'application/json')\n  t.assert.deepStrictEqual(trailers, { 'content-md5': 'test' })\n\n  const jsonResponse = JSON.parse(await getResponse(body))\n  t.assert.deepStrictEqual(jsonResponse, {\n    foo: 'bar'\n  })\n})\n\n// https://github.com/nodejs/undici/issues/1546\ntest('MockPool - correct errors when consuming invalid JSON body', async (t) => {\n  t.plan(1)\n\n  const oldDispatcher = getGlobalDispatcher()\n\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n\n  after(() => setGlobalDispatcher(oldDispatcher))\n\n  const mockPool = mockAgent.get('https://google.com')\n  mockPool.intercept({\n    path: 'https://google.com'\n  }).reply(200, 'it\\'s just a text')\n\n  const { body } = await request('https://google.com')\n  await t.assert.rejects(body.json(), SyntaxError)\n})\n\ntest('MockPool - allows matching headers in fetch', async (t) => {\n  t.plan(2)\n\n  const oldDispatcher = getGlobalDispatcher()\n\n  const baseUrl = 'http://localhost:9999'\n  const mockAgent = new MockAgent()\n  mockAgent.disableNetConnect()\n  setGlobalDispatcher(mockAgent)\n\n  after(async () => {\n    await mockAgent.close()\n    setGlobalDispatcher(oldDispatcher)\n  })\n\n  const pool = mockAgent.get(baseUrl)\n  pool.intercept({\n    path: '/foo',\n    method: 'GET',\n    headers: {\n      accept: 'application/json'\n    }\n  }).reply(200, { ok: 1 }).times(3)\n\n  await fetch(`${baseUrl}/foo`, {\n    headers: {\n      accept: 'application/json'\n    }\n  })\n\n  // no 'accept: application/json' header sent, not matched\n  await t.assert.rejects(fetch(`${baseUrl}/foo`))\n\n  // not 'accept: application/json', not matched\n  await t.assert.rejects(fetch(`${baseUrl}/foo`, {\n    headers: {\n      accept: 'text/plain'\n    }\n  }), new TypeError('fetch failed'))\n})\n\ntest('MockPool - cleans mocks', async (t) => {\n  t.plan(4)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await promisify(server.listen.bind(server))(0)\n\n  const baseUrl = `http://localhost:${server.address().port}`\n\n  const mockAgent = new MockAgent()\n  after(() => mockAgent.close())\n\n  const mockPool = mockAgent.get(baseUrl)\n  t.assert.ok(mockPool instanceof MockPool)\n  setGlobalDispatcher(mockPool)\n\n  mockPool.intercept({\n    path: '/foo',\n    method: 'GET'\n  }).reply(500, () => {\n    t.assert.fail('should not be called')\n  })\n\n  mockPool.cleanMocks()\n\n  t.assert.strictEqual(mockPool[kDispatches].length, 0)\n\n  const { statusCode, body } = await request(`${baseUrl}/foo`, {\n    method: 'GET'\n  })\n  t.assert.strictEqual(statusCode, 200)\n\n  const response = await getResponse(body)\n  t.assert.deepStrictEqual(response, 'hello')\n})\n"
  },
  {
    "path": "test/mock-scope.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { MockScope } = require('../lib/mock/mock-interceptor')\nconst { InvalidArgumentError } = require('../lib/core/errors')\n\ndescribe('MockScope - delay', () => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockScope = new MockScope({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockScope.delay(200)\n    t.assert.ok(result instanceof MockScope)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(4)\n\n    const mockScope = new MockScope({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockScope.delay(), new InvalidArgumentError('waitInMs must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.delay(200.1), new InvalidArgumentError('waitInMs must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.delay(0), new InvalidArgumentError('waitInMs must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.delay(-1), new InvalidArgumentError('waitInMs must be a valid integer > 0'))\n  })\n})\n\ndescribe('MockScope - persist', () => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockScope = new MockScope({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockScope.persist()\n    t.assert.ok(result instanceof MockScope)\n  })\n})\n\ndescribe('MockScope - times', t => {\n  test('should return MockScope', t => {\n    t.plan(1)\n    const mockScope = new MockScope({\n      path: '',\n      method: ''\n    }, [])\n    const result = mockScope.times(200)\n    t.assert.ok(result instanceof MockScope)\n  })\n\n  test('should error if passed options invalid', t => {\n    t.plan(4)\n\n    const mockScope = new MockScope({\n      path: '',\n      method: ''\n    }, [])\n    t.assert.throws(() => mockScope.times(), new InvalidArgumentError('repeatTimes must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.times(200.1), new InvalidArgumentError('repeatTimes must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.times(0), new InvalidArgumentError('repeatTimes must be a valid integer > 0'))\n    t.assert.throws(() => mockScope.times(-1), new InvalidArgumentError('repeatTimes must be a valid integer > 0'))\n  })\n})\n"
  },
  {
    "path": "test/mock-utils.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { MockNotMatchedError } = require('../lib/mock/mock-errors')\nconst {\n  deleteMockDispatch,\n  getMockDispatch,\n  getResponseData,\n  getStatusText,\n  getHeaderByName,\n  buildHeadersFromArray,\n  normalizeSearchParams,\n  normalizeOrigin\n} = require('../lib/mock/mock-utils')\n\ntest('deleteMockDispatch - should do nothing if not able to find mock dispatch', (t) => {\n  t.plan(1)\n\n  const key = {\n    url: 'url',\n    path: 'path',\n    method: 'method',\n    body: 'body'\n  }\n\n  t.assert.doesNotThrow(() => deleteMockDispatch([], key))\n})\n\ndescribe('getMockDispatch', () => {\n  test('it should find a mock dispatch', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        consumed: false\n      }\n    ]\n\n    const result = getMockDispatch(dispatches, {\n      path: 'path',\n      method: 'method'\n    })\n    t.assert.deepStrictEqual(result, {\n      path: 'path',\n      method: 'method',\n      consumed: false\n    })\n  })\n\n  test('it should skip consumed dispatches', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        consumed: true\n      },\n      {\n        path: 'path',\n        method: 'method',\n        consumed: false\n      }\n    ]\n\n    const result = getMockDispatch(dispatches, {\n      path: 'path',\n      method: 'method'\n    })\n    t.assert.deepStrictEqual(result, {\n      path: 'path',\n      method: 'method',\n      consumed: false\n    })\n  })\n\n  test('it should throw if dispatch not found', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        consumed: false\n      }\n    ]\n\n    t.assert.throws(() => getMockDispatch(dispatches, {\n      path: 'wrong',\n      method: 'wrong'\n    }), new MockNotMatchedError('Mock dispatch not matched for path \\'wrong\\''))\n  })\n\n  test('it should throw if no dispatch matches method', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        consumed: false\n      }\n    ]\n\n    t.assert.throws(() => getMockDispatch(dispatches, {\n      path: 'path',\n      method: 'wrong'\n    }), new MockNotMatchedError('Mock dispatch not matched for method \\'wrong\\' on path \\'path\\''))\n  })\n\n  test('it should throw if no dispatch matches body', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        body: 'body',\n        consumed: false\n      }\n    ]\n\n    t.assert.throws(() => getMockDispatch(dispatches, {\n      path: 'path',\n      method: 'method',\n      body: 'wrong'\n    }), new MockNotMatchedError('Mock dispatch not matched for body \\'wrong\\' on path \\'path\\''))\n  })\n\n  test('it should throw if no dispatch matches headers', (t) => {\n    t.plan(1)\n    const dispatches = [\n      {\n        path: 'path',\n        method: 'method',\n        body: 'body',\n        headers: { key: 'value' },\n        consumed: false\n      }\n    ]\n\n    t.assert.throws(() => getMockDispatch(dispatches, {\n      path: 'path',\n      method: 'method',\n      body: 'body',\n      headers: { key: 'wrong' }\n    }), new MockNotMatchedError('Mock dispatch not matched for headers \\'{\"key\":\"wrong\"}\\' on path \\'path\\''))\n  })\n})\n\ndescribe('getResponseData', () => {\n  test('it should stringify objects', (t) => {\n    t.plan(1)\n    const responseData = getResponseData({ str: 'string', num: 42 })\n    t.assert.strictEqual(responseData, '{\"str\":\"string\",\"num\":42}')\n  })\n\n  test('it should return strings untouched', (t) => {\n    t.plan(1)\n    const responseData = getResponseData('test')\n    t.assert.strictEqual(responseData, 'test')\n  })\n\n  test('it should return buffers untouched', (t) => {\n    t.plan(1)\n    const responseData = getResponseData(Buffer.from('test'))\n    t.assert.ok(Buffer.isBuffer(responseData))\n  })\n\n  test('it should return Uint8Array untouched', (t) => {\n    t.plan(1)\n    const responseData = getResponseData(new TextEncoder().encode('{\"test\":true}'))\n    t.assert.ok(responseData instanceof Uint8Array)\n  })\n\n  test('it should return ArrayBuffers untouched', (t) => {\n    t.plan(1)\n    const responseData = getResponseData(new TextEncoder().encode('{\"test\":true}').buffer)\n    t.assert.ok(responseData instanceof ArrayBuffer)\n  })\n\n  test('it should handle undefined', (t) => {\n    t.plan(1)\n    const responseData = getResponseData(undefined)\n    t.assert.strictEqual(responseData, '')\n  })\n})\n\ntest('getStatusText', (t) => {\n  t.plan(64)\n\n  for (const statusCode of [\n    100, 101, 102, 103, 200, 201, 202, 203,\n    204, 205, 206, 207, 208, 226, 300, 301,\n    302, 303, 304, 305, 306, 307, 308, 400,\n    401, 402, 403, 404, 405, 406, 407, 408,\n    409, 410, 411, 412, 413, 414, 415, 416,\n    417, 418, 421, 422, 423, 424, 425, 426,\n    428, 429, 431, 451, 500, 501, 502, 503,\n    504, 505, 506, 507, 508, 510, 511\n  ]) {\n    t.assert.ok(getStatusText(statusCode))\n  }\n\n  t.assert.strictEqual(getStatusText(420), 'unknown')\n})\n\ntest('getHeaderByName', (t) => {\n  t.plan(6)\n\n  const headersRecord = {\n    key: 'value'\n  }\n\n  t.assert.strictEqual(getHeaderByName(headersRecord, 'key'), 'value')\n  t.assert.strictEqual(getHeaderByName(headersRecord, 'anotherKey'), undefined)\n\n  const headersArray = ['key', 'value']\n\n  t.assert.strictEqual(getHeaderByName(headersArray, 'key'), 'value')\n  t.assert.strictEqual(getHeaderByName(headersArray, 'anotherKey'), undefined)\n\n  const { Headers } = require('../index')\n\n  const headers = new Headers([\n    ['key', 'value']\n  ])\n\n  t.assert.strictEqual(getHeaderByName(headers, 'key'), 'value')\n  t.assert.strictEqual(getHeaderByName(headers, 'anotherKey'), null)\n})\n\ndescribe('buildHeadersFromArray', () => {\n  test('it should build headers from array', (t) => {\n    t.plan(2)\n\n    const headers = buildHeadersFromArray([\n      'key', 'value'\n    ])\n\n    t.assert.deepStrictEqual(Object.keys(headers).length, 1)\n    t.assert.strictEqual(headers.key, 'value')\n  })\n})\n\ndescribe('normalizeQueryParams', () => {\n  test('it should handle basic cases', (t) => {\n    t.plan(4)\n\n    t.assert.deepStrictEqual(normalizeSearchParams('').toString(), '')\n    t.assert.deepStrictEqual(normalizeSearchParams('a').toString(), 'a=')\n    t.assert.deepStrictEqual(normalizeSearchParams('b=2&c=3&a=1').toString(), 'b=2&c=3&a=1')\n    t.assert.deepStrictEqual(normalizeSearchParams('lang=en_EN&id=123').toString(), 'lang=en_EN&id=123')\n  })\n\n  // https://github.com/nodejs/undici/issues/4146\n  test('it should handle multiple values set using different syntaxes', (t) => {\n    t.plan(3)\n\n    t.assert.deepStrictEqual(normalizeSearchParams('a=1&a=2&a=3').toString(), 'a=1&a=2&a=3')\n    t.assert.deepStrictEqual(normalizeSearchParams('a[]=1&a[]=2&a[]=3').toString(), 'a=1&a=2&a=3')\n    t.assert.deepStrictEqual(normalizeSearchParams('a=1,2,3').toString(), 'a=1&a=2&a=3')\n  })\n\n  test('should handle edge case scenarios', (t) => {\n    t.plan(4)\n\n    t.assert.deepStrictEqual(normalizeSearchParams('a=\"b[]\"').toString(), `a=${encodeURIComponent('\"b[]\"')}`)\n    t.assert.deepStrictEqual(normalizeSearchParams('a=\"1,2,3\"').toString(), `a=${encodeURIComponent('\"1,2,3\"')}`)\n    const encodedSingleQuote = '%27'\n    t.assert.deepStrictEqual(normalizeSearchParams(\"a='b[]'\").toString(), `a=${encodedSingleQuote}${encodeURIComponent('b[]')}${encodedSingleQuote}`)\n    t.assert.deepStrictEqual(normalizeSearchParams(\"a='1,2,3'\").toString(), `a=${encodedSingleQuote}${encodeURIComponent('1,2,3')}${encodedSingleQuote}`)\n  })\n})\n\ndescribe('normalizeOrigin', () => {\n  test('should normalize hostname to lowercase for string origins', (t) => {\n    t.plan(4)\n\n    t.assert.strictEqual(normalizeOrigin('http://Example.com'), 'http://example.com')\n    t.assert.strictEqual(normalizeOrigin('http://EXAMPLE.COM'), 'http://example.com')\n    t.assert.strictEqual(normalizeOrigin('https://Api.Example.com'), 'https://api.example.com')\n    t.assert.strictEqual(normalizeOrigin('http://MyEndpoint'), 'http://myendpoint')\n  })\n\n  test('should normalize hostname to lowercase for URL objects', (t) => {\n    t.plan(4)\n\n    t.assert.strictEqual(normalizeOrigin(new URL('http://Example.com')), 'http://example.com')\n    t.assert.strictEqual(normalizeOrigin(new URL('http://EXAMPLE.COM')), 'http://example.com')\n    t.assert.strictEqual(normalizeOrigin(new URL('https://Api.Example.com')), 'https://api.example.com')\n    t.assert.strictEqual(normalizeOrigin(new URL('http://MyEndpoint')), 'http://myendpoint')\n  })\n\n  test('should preserve port numbers', (t) => {\n    t.plan(3)\n\n    t.assert.strictEqual(normalizeOrigin('http://Example.com:8080'), 'http://example.com:8080')\n    t.assert.strictEqual(normalizeOrigin(new URL('http://Example.com:3000')), 'http://example.com:3000')\n    t.assert.strictEqual(normalizeOrigin(new URL('https://Test.com:8443')), 'https://test.com:8443')\n  })\n\n  test('should return RegExp matchers as-is', (t) => {\n    t.plan(1)\n\n    const regex = /http:\\/\\/example\\.com/\n    t.assert.strictEqual(normalizeOrigin(regex), regex)\n  })\n\n  test('should return function matchers as-is', (t) => {\n    t.plan(1)\n\n    const fn = (origin) => origin === 'http://example.com'\n    t.assert.strictEqual(normalizeOrigin(fn), fn)\n  })\n\n  test('should return other non-string, non-URL types as-is', (t) => {\n    t.plan(4)\n\n    const obj = { origin: 'http://example.com' }\n    const num = 123\n    const bool = true\n    const nullValue = null\n\n    t.assert.strictEqual(normalizeOrigin(obj), obj)\n    t.assert.strictEqual(normalizeOrigin(num), num)\n    t.assert.strictEqual(normalizeOrigin(bool), bool)\n    t.assert.strictEqual(normalizeOrigin(nullValue), nullValue)\n  })\n\n  test('should handle invalid URLs gracefully', (t) => {\n    t.plan(2)\n\n    // Invalid URL strings should be returned as-is\n    t.assert.strictEqual(normalizeOrigin('not-a-url'), 'not-a-url')\n    t.assert.strictEqual(normalizeOrigin('://invalid'), '://invalid')\n  })\n\n  test('should handle IPv4 addresses', (t) => {\n    t.plan(2)\n\n    t.assert.strictEqual(normalizeOrigin('http://192.168.1.1'), 'http://192.168.1.1')\n    t.assert.strictEqual(normalizeOrigin('http://127.0.0.1:3000'), 'http://127.0.0.1:3000')\n  })\n\n  test('should handle IPv6 addresses', (t) => {\n    t.plan(2)\n\n    t.assert.strictEqual(normalizeOrigin('http://[::1]'), 'http://[::1]')\n    t.assert.strictEqual(normalizeOrigin('http://[2001:db8::1]:8080'), 'http://[2001:db8::1]:8080')\n  })\n\n  test('should handle localhost with different cases', (t) => {\n    t.plan(3)\n\n    t.assert.strictEqual(normalizeOrigin('http://LocalHost'), 'http://localhost')\n    t.assert.strictEqual(normalizeOrigin('http://LOCALHOST:3000'), 'http://localhost:3000')\n    t.assert.strictEqual(normalizeOrigin(new URL('http://LocalHost')), 'http://localhost')\n  })\n\n  test('should handle subdomains with mixed case', (t) => {\n    t.plan(3)\n\n    t.assert.strictEqual(normalizeOrigin('http://Api.Example.Com'), 'http://api.example.com')\n    t.assert.strictEqual(normalizeOrigin('https://WWW.Example.COM'), 'https://www.example.com')\n    t.assert.strictEqual(normalizeOrigin(new URL('http://Sub.Domain.Example.Com')), 'http://sub.domain.example.com')\n  })\n\n  test('should handle paths in URL objects (should only normalize origin part)', (t) => {\n    t.plan(2)\n\n    // URL objects with paths should still only return the origin\n    const url1 = new URL('http://Example.com/path/to/resource')\n    t.assert.strictEqual(normalizeOrigin(url1), 'http://example.com')\n\n    const url2 = new URL('https://Api.Example.com:8080/api/v1')\n    t.assert.strictEqual(normalizeOrigin(url2), 'https://api.example.com:8080')\n  })\n})\n"
  },
  {
    "path": "test/no-strict-content-length.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { ok } = require('node:assert')\nconst { test, after, describe } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { wrapWithAsyncIterable } = require('./utils/async-iterators')\n\ndescribe('strictContentLength: false', () => {\n  const emitWarningOriginal = process.emitWarning\n  let emitWarningCalled = false\n\n  process.emitWarning = function () {\n    emitWarningCalled = true\n  }\n\n  function assertEmitWarningCalledAndReset () {\n    ok(emitWarningCalled)\n    emitWarningCalled = false\n  }\n\n  after(() => {\n    process.emitWarning = emitWarningOriginal\n  })\n\n  test('request invalid content-length', async (t) => {\n    t = tspl(t, { plan: 8 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: 'asd'\n      }, (err, data) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: 'asdasdasdasdasdasda'\n      }, (err, data) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: Buffer.alloc(9)\n      }, (err, data) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: Buffer.alloc(11)\n      }, (err, data) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'HEAD',\n        headers: {\n          'content-length': 10\n        }\n      }, (err, data) => {\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        headers: {\n          'content-length': 0\n        }\n      }, (err, data) => {\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        headers: {\n          'content-length': 4\n        },\n        body: new Readable({\n          read () {\n            this.push('asd')\n            this.push(null)\n          }\n        })\n      }, (err, data) => {\n        t.ifError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        headers: {\n          'content-length': 4\n        },\n        body: new Readable({\n          read () {\n            this.push('asasdasdasdd')\n            this.push(null)\n          }\n        })\n      }, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    await t.completed\n  })\n\n  test('request streaming content-length less than body size', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 2\n        },\n        body: new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('abcd')\n              this.push(null)\n            })\n          }\n        })\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n\n    await t.completed\n  })\n\n  test('request streaming content-length greater than body size', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('abcd')\n              this.push(null)\n            })\n          }\n        })\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n\n    await t.completed\n  })\n\n  test('request streaming data when content-length=0', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 0\n        },\n        body: new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('asdasdasdkajsdnasdkjasnd')\n              this.push(null)\n            })\n          }\n        })\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n\n    await t.completed\n  })\n\n  test('request async iterating content-length less than body size', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 2\n        },\n        body: wrapWithAsyncIterable(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('abcd')\n              this.push(null)\n            })\n          }\n        }))\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n\n    await t.completed\n  })\n\n  test('request async iterator content-length greater than body size', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 10\n        },\n        body: wrapWithAsyncIterable(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('abcd')\n              this.push(null)\n            })\n          }\n        }))\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n    await t.completed\n  })\n\n  test('request async iterator data when content-length=0', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        strictContentLength: false\n      })\n      after(() => client.close())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        headers: {\n          'content-length': 0\n        },\n        body: wrapWithAsyncIterable(new Readable({\n          read () {\n            setImmediate(() => {\n              this.push('asdasdasdkajsdnasdkjasnd')\n              this.push(null)\n            })\n          }\n        }))\n      }, (err) => {\n        assertEmitWarningCalledAndReset()\n        t.ifError(err)\n      })\n    })\n    await t.completed\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 - 2020 Node Fetch Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "test/node-fetch/headers.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { describe, it } = require('node:test')\nconst { format } = require('node:util')\nconst { Headers } = require('../../index.js')\n\ndescribe('Headers', () => {\n  it('should have attributes conforming to Web IDL', () => {\n    const headers = new Headers()\n    assert.strictEqual(Object.getOwnPropertyNames(headers).length, 0)\n    const enumerableProperties = []\n\n    for (const property in headers) {\n      enumerableProperties.push(property)\n    }\n\n    for (const toCheck of [\n      'append',\n      'delete',\n      'entries',\n      'forEach',\n      'get',\n      'has',\n      'keys',\n      'set',\n      'values'\n    ]) {\n      assert.strictEqual(enumerableProperties.includes(toCheck), true)\n    }\n  })\n\n  it('should allow iterating through all headers with forEach', () => {\n    const headers = new Headers([\n      ['b', '2'],\n      ['c', '4'],\n      ['b', '3'],\n      ['a', '1']\n    ])\n    assert.strictEqual(typeof headers.forEach, 'function')\n\n    const result = []\n    for (const [key, value] of headers.entries()) {\n      result.push([key, value])\n    }\n\n    assert.deepStrictEqual(result, [\n      ['a', '1'],\n      ['b', '2, 3'],\n      ['c', '4']\n    ])\n  })\n\n  it('should be iterable with forEach', () => {\n    const headers = new Headers()\n    headers.append('Accept', 'application/json')\n    headers.append('Accept', 'text/plain')\n    headers.append('Content-Type', 'text/html')\n\n    const results = []\n    headers.forEach((value, key, object) => {\n      results.push({ value, key, object })\n    })\n\n    assert.strictEqual(results.length, 2)\n    assert.deepStrictEqual(results[0], { key: 'accept', value: 'application/json, text/plain', object: headers })\n    assert.deepStrictEqual(results[1], { key: 'content-type', value: 'text/html', object: headers })\n  })\n\n  it.skip('should set \"this\" to undefined by default on forEach', () => {\n    const headers = new Headers({ Accept: 'application/json' })\n    headers.forEach(function () {\n      assert.strictEqual(this, undefined)\n    })\n  })\n\n  it('should accept thisArg as a second argument for forEach', () => {\n    const headers = new Headers({ Accept: 'application/json' })\n    const thisArg = {}\n    headers.forEach(function () {\n      assert.strictEqual(this, thisArg)\n    }, thisArg)\n  })\n\n  it('should allow iterating through all headers with for-of loop', () => {\n    const headers = new Headers([\n      ['b', '2'],\n      ['c', '4'],\n      ['a', '1']\n    ])\n    headers.append('b', '3')\n    assert.strictEqual(typeof headers[Symbol.iterator], 'function')\n\n    const result = []\n    for (const pair of headers) {\n      result.push(pair)\n    }\n\n    assert.deepStrictEqual(result, [\n      ['a', '1'],\n      ['b', '2, 3'],\n      ['c', '4']\n    ])\n  })\n\n  it('should allow iterating through all headers with entries()', () => {\n    const headers = new Headers([\n      ['b', '2'],\n      ['c', '4'],\n      ['a', '1']\n    ])\n    headers.append('b', '3')\n\n    assert.strictEqual(typeof headers.entries, 'function')\n    assert.strictEqual(typeof headers.entries()[Symbol.iterator], 'function')\n\n    const entries = headers.entries()\n    assert.strictEqual(typeof entries.next, 'function')\n    assert.deepStrictEqual(entries.next().value, ['a', '1'])\n    assert.strictEqual(typeof entries.next, 'function')\n    assert.deepStrictEqual(entries.next().value, ['b', '2, 3'])\n    assert.strictEqual(typeof entries.next, 'function')\n    assert.deepStrictEqual(entries.next().value, ['c', '4'])\n\n    assert.deepStrictEqual([...headers.entries()], [\n      ['a', '1'],\n      ['b', '2, 3'],\n      ['c', '4']\n    ])\n  })\n\n  it('should allow iterating through all headers with keys()', () => {\n    const headers = new Headers([\n      ['b', '2'],\n      ['c', '4'],\n      ['a', '1']\n    ])\n    headers.append('b', '3')\n\n    assert.strictEqual(typeof headers.keys, 'function')\n    assert.strictEqual(typeof headers.keys()[Symbol.iterator], 'function')\n\n    const keys = headers.keys()\n    assert.strictEqual(typeof keys.next, 'function')\n    assert.strictEqual(keys.next().value, 'a')\n    assert.strictEqual(typeof keys.next, 'function')\n    assert.strictEqual(keys.next().value, 'b')\n    assert.strictEqual(typeof keys.next, 'function')\n    assert.strictEqual(keys.next().value, 'c')\n\n    assert.deepStrictEqual([...headers.keys()], ['a', 'b', 'c'])\n  })\n\n  it('should allow iterating through all headers with values()', () => {\n    const headers = new Headers([\n      ['b', '2'],\n      ['c', '4'],\n      ['a', '1']\n    ])\n    headers.append('b', '3')\n\n    assert.strictEqual(typeof headers.values, 'function')\n    assert.strictEqual(typeof headers.values()[Symbol.iterator], 'function')\n\n    const values = headers.values()\n    assert.strictEqual(typeof values.next, 'function')\n    assert.strictEqual(values.next().value, '1')\n    assert.strictEqual(typeof values.next, 'function')\n    assert.strictEqual(values.next().value, '2, 3')\n    assert.strictEqual(typeof values.next, 'function')\n    assert.strictEqual(values.next().value, '4')\n\n    assert.deepStrictEqual([...headers.values()], ['1', '2, 3', '4'])\n  })\n\n  it('should reject illegal header', () => {\n    const headers = new Headers()\n\n    assert.throws(() => new Headers({ 'He y': 'ok' }), TypeError)\n    assert.throws(() => new Headers({ 'Hé-y': 'ok' }), TypeError)\n    assert.throws(() => new Headers({ 'He-y': 'ăk' }), TypeError)\n    assert.throws(() => headers.append('Hé-y', 'ok'), TypeError)\n    assert.throws(() => headers.delete('Hé-y'), TypeError)\n    assert.throws(() => headers.get('Hé-y'), TypeError)\n    assert.throws(() => headers.has('Hé-y'), TypeError)\n    assert.throws(() => headers.set('Hé-y', 'ok'), TypeError)\n    // Should reject empty header\n    assert.throws(() => headers.append('', 'ok'), TypeError)\n  })\n\n  it.skip('should ignore unsupported attributes while reading headers', () => {\n    const FakeHeader = function () { }\n    // Prototypes are currently ignored\n    // This might change in the future: #181\n    FakeHeader.prototype.z = 'fake'\n\n    const res = new FakeHeader()\n    res.a = 'string'\n    res.b = ['1', '2']\n    res.c = ''\n    res.d = []\n    res.e = 1\n    res.f = [1, 2]\n    res.g = { a: 1 }\n    res.h = undefined\n    res.i = null\n    res.j = Number.NaN\n    res.k = true\n    res.l = false\n    res.m = Buffer.from('test')\n\n    const h1 = new Headers(res)\n    h1.set('n', [1, 2])\n    h1.append('n', ['3', 4])\n\n    const h1Raw = h1.raw()\n\n    assert.strictEqual(h1Raw.a.includes('string'), true)\n    assert.strictEqual(h1Raw.b.includes('1,2'), true)\n    assert.strictEqual(h1Raw.c.includes(''), true)\n    assert.strictEqual(h1Raw.d.includes(''), true)\n    assert.strictEqual(h1Raw.e.includes('1'), true)\n    assert.strictEqual(h1Raw.f.includes('1,2'), true)\n    assert.strictEqual(h1Raw.g.includes('[object Object]'), true)\n    assert.strictEqual(h1Raw.h.includes('undefined'), true)\n    assert.strictEqual(h1Raw.i.includes('null'), true)\n    assert.strictEqual(h1Raw.j.includes('NaN'), true)\n    assert.strictEqual(h1Raw.k.includes('true'), true)\n    assert.strictEqual(h1Raw.l.includes('false'), true)\n    assert.strictEqual(h1Raw.m.includes('test'), true)\n    assert.strictEqual(h1Raw.n.includes('1,2'), true)\n    assert.strictEqual(h1Raw.n.includes('3,4'), true)\n\n    assert.strictEqual(h1Raw.z, undefined)\n  })\n\n  it.skip('should wrap headers', () => {\n    const h1 = new Headers({\n      a: '1'\n    })\n    const h1Raw = h1.raw()\n\n    const h2 = new Headers(h1)\n    h2.set('b', '1')\n    const h2Raw = h2.raw()\n\n    const h3 = new Headers(h2)\n    h3.append('a', '2')\n    const h3Raw = h3.raw()\n\n    assert.strictEqual(h1Raw.a.includes('1'), true)\n    assert.strictEqual(h1Raw.a.includes('2'), false)\n\n    assert.strictEqual(h2Raw.a.includes('1'), true)\n    assert.strictEqual(h2Raw.a.includes('2'), false)\n    assert.strictEqual(h2Raw.b.includes('1'), true)\n\n    assert.strictEqual(h3Raw.a.includes('1'), true)\n    assert.strictEqual(h3Raw.a.includes('2'), true)\n    assert.strictEqual(h3Raw.b.includes('1'), true)\n  })\n\n  it('should accept headers as an iterable of tuples', () => {\n    let headers\n\n    headers = new Headers([\n      ['a', '1'],\n      ['b', '2'],\n      ['a', '3']\n    ])\n    assert.strictEqual(headers.get('a'), '1, 3')\n    assert.strictEqual(headers.get('b'), '2')\n\n    headers = new Headers([\n      new Set(['a', '1']),\n      ['b', '2'],\n      new Map([['a', null], ['3', null]]).keys()\n    ])\n    assert.strictEqual(headers.get('a'), '1, 3')\n    assert.strictEqual(headers.get('b'), '2')\n\n    headers = new Headers(new Map([\n      ['a', '1'],\n      ['b', '2']\n    ]))\n    assert.strictEqual(headers.get('a'), '1')\n    assert.strictEqual(headers.get('b'), '2')\n  })\n\n  it('should throw a TypeError if non-tuple exists in a headers initializer', () => {\n    assert.throws(() => new Headers([['b', '2', 'huh?']]), TypeError)\n    assert.throws(() => new Headers(['b2']), TypeError)\n    assert.throws(() => new Headers('b2'), TypeError)\n    assert.throws(() => new Headers({ [Symbol.iterator]: 42 }), TypeError)\n  })\n\n  it.skip('should use a custom inspect function', () => {\n    const headers = new Headers([\n      ['Host', 'thehost'],\n      ['Host', 'notthehost'],\n      ['a', '1'],\n      ['b', '2'],\n      ['a', '3']\n    ])\n\n    assert.strictEqual(format(headers), \"{ a: [ '1', '3' ], b: '2', host: 'thehost' }\")\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/main.js",
    "content": "'use strict'\n\n// Test tools\nconst assert = require('node:assert')\nconst { describe, it, before, beforeEach, after } = require('node:test')\nconst { setTimeout: delay } = require('node:timers/promises')\nconst zlib = require('node:zlib')\nconst stream = require('node:stream')\nconst vm = require('node:vm')\nconst crypto = require('node:crypto')\n\nconst {\n  fetch,\n  Headers,\n  Request,\n  FormData,\n  Response,\n  setGlobalDispatcher,\n  Agent\n} = require('../../index.js')\nconst HeadersOrig = require('../../lib/web/fetch/headers.js').Headers\nconst ResponseOrig = require('../../lib/web/fetch/response.js').Response\nconst RequestOrig = require('../../lib/web/fetch/request.js').Request\nconst TestServer = require('./utils/server.js')\nconst { createServer } = require('node:http')\nconst { default: tspl } = require('@matteo.collina/tspl')\n\nconst {\n  Uint8Array: VMUint8Array\n} = vm.runInNewContext('this')\n\ndescribe('node-fetch', () => {\n  const local = new TestServer()\n  let base\n\n  before(async () => {\n    await local.start()\n    setGlobalDispatcher(new Agent({\n      connect: {\n        rejectUnauthorized: false\n      }\n    }))\n    base = `http://${local.hostname}:${local.port}/`\n  })\n\n  after(async () => {\n    return local.stop()\n  })\n\n  it('should return a promise', () => {\n    const url = `${base}hello`\n    const p = fetch(url)\n    assert.ok(p instanceof Promise)\n    assert.strictEqual(typeof p.then, 'function')\n  })\n\n  it('should expose Headers, Response and Request constructors', () => {\n    assert.strictEqual(Headers, HeadersOrig)\n    assert.strictEqual(Response, ResponseOrig)\n    assert.strictEqual(Request, RequestOrig)\n  })\n\n  it('should support proper toString output for Headers, Response and Request objects', () => {\n    assert.strictEqual(new Headers().toString(), '[object Headers]')\n    assert.strictEqual(new Response().toString(), '[object Response]')\n    assert.strictEqual(new Request(base).toString(), '[object Request]')\n  })\n  // TODO Should we reflect the input?\n  it('should reject with error if url is protocol relative', () => {\n    const url = '//example.com/'\n    return assert.rejects(fetch(url), new TypeError('Failed to parse URL from //example.com/'))\n  })\n\n  it('should reject with error if url is relative path', () => {\n    const url = '/some/path'\n    return assert.rejects(fetch(url), new TypeError('Failed to parse URL from /some/path'))\n  })\n\n  // TODO: This seems odd\n  it('should reject with error if protocol is unsupported', () => {\n    const url = 'ftp://example.com/'\n    return assert.rejects(fetch(url), new TypeError('fetch failed'))\n  })\n\n  it('should reject with error on network failure', { timeout: 5000 }, function () {\n    const url = 'http://localhost:50000/'\n    return assert.rejects(fetch(url), new TypeError('fetch failed'))\n  })\n\n  it('should resolve into response', () => {\n    const url = `${base}hello`\n    return fetch(url).then(res => {\n      assert.ok(res instanceof Response)\n      assert.ok(res.headers instanceof Headers)\n      assert.ok(res.body instanceof ReadableStream)\n      assert.strictEqual(res.bodyUsed, false)\n\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.ok, true)\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.statusText, 'OK')\n    })\n  })\n\n  it('Response.redirect should resolve into response', () => {\n    const res = Response.redirect('http://localhost')\n    assert.ok(res instanceof Response)\n    assert.ok(res.headers instanceof Headers)\n    assert.strictEqual(res.headers.get('location'), 'http://localhost/')\n    assert.strictEqual(res.status, 302)\n  })\n\n  it('Response.redirect /w invalid url should fail', () => {\n    assert.throws(() => {\n      Response.redirect('localhost')\n    })\n  })\n\n  it('Response.redirect /w invalid status should fail', () => {\n    assert.throws(() => {\n      Response.redirect('http://localhost', 200)\n    })\n  })\n\n  it('should accept plain text response', () => {\n    const url = `${base}plain`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(res.bodyUsed, true)\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'text')\n      })\n    })\n  })\n\n  it('should accept html response (like plain text)', () => {\n    const url = `${base}html`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/html')\n      return res.text().then(result => {\n        assert.strictEqual(res.bodyUsed, true)\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '<html></html>')\n      })\n    })\n  })\n\n  it('should accept json response', () => {\n    const url = `${base}json`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'application/json')\n      return res.json().then(result => {\n        assert.strictEqual(res.bodyUsed, true)\n        assert.strictEqual(typeof result, 'object')\n        assert.deepStrictEqual(result, { name: 'value' })\n      })\n    })\n  })\n\n  it('should send request with custom headers', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: { 'x-custom-header': 'abc' }\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.headers['x-custom-header'], 'abc')\n    })\n  })\n\n  it('should send request with custom headers array', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: { 'x-custom-header': ['abc'] }\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.headers['x-custom-header'], 'abc')\n    })\n  })\n\n  it('should send request with multi-valued headers', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: { 'x-custom-header': ['abc', '123'] }\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.headers['x-custom-header'], 'abc,123')\n    })\n  })\n\n  it('should accept headers instance', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: new Headers({ 'x-custom-header': 'abc' })\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.headers['x-custom-header'], 'abc')\n    })\n  })\n\n  it('should follow redirect code 301', () => {\n    const url = `${base}redirect/301`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.ok, true)\n    })\n  })\n\n  it('should follow redirect code 302', () => {\n    const url = `${base}redirect/302`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should follow redirect code 303', () => {\n    const url = `${base}redirect/303`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should follow redirect code 307', () => {\n    const url = `${base}redirect/307`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should follow redirect code 308', () => {\n    const url = `${base}redirect/308`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should follow redirect chain', () => {\n    const url = `${base}redirect/chain`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should follow POST request redirect code 301 with GET', () => {\n    const url = `${base}redirect/301`\n    const options = {\n      method: 'POST',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(result => {\n        assert.strictEqual(result.method, 'GET')\n        assert.strictEqual(result.body, '')\n      })\n    })\n  })\n\n  it('should follow PATCH request redirect code 301 with PATCH', () => {\n    const url = `${base}redirect/301`\n    const options = {\n      method: 'PATCH',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(res => {\n        assert.strictEqual(res.method, 'PATCH')\n        assert.strictEqual(res.body, 'a=1')\n      })\n    })\n  })\n\n  it('should follow POST request redirect code 302 with GET', () => {\n    const url = `${base}redirect/302`\n    const options = {\n      method: 'POST',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(result => {\n        assert.strictEqual(result.method, 'GET')\n        assert.strictEqual(result.body, '')\n      })\n    })\n  })\n\n  it('should follow PATCH request redirect code 302 with PATCH', () => {\n    const url = `${base}redirect/302`\n    const options = {\n      method: 'PATCH',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(res => {\n        assert.strictEqual(res.method, 'PATCH')\n        assert.strictEqual(res.body, 'a=1')\n      })\n    })\n  })\n\n  it('should follow redirect code 303 with GET', () => {\n    const url = `${base}redirect/303`\n    const options = {\n      method: 'PUT',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(result => {\n        assert.strictEqual(result.method, 'GET')\n        assert.strictEqual(result.body, '')\n      })\n    })\n  })\n\n  it('should follow PATCH request redirect code 307 with PATCH', () => {\n    const url = `${base}redirect/307`\n    const options = {\n      method: 'PATCH',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n      return res.json().then(result => {\n        assert.strictEqual(result.method, 'PATCH')\n        assert.strictEqual(result.body, 'a=1')\n      })\n    })\n  })\n\n  it('should not follow non-GET redirect if body is a readable stream', () => {\n    const url = `${base}redirect/307`\n    const options = {\n      method: 'PATCH',\n      body: stream.Readable.from('tada')\n    }\n    return assert.rejects(fetch(url, options), new TypeError('RequestInit: duplex option is required when sending a body.'))\n  })\n\n  it('should obey maximum redirect, reject case', () => {\n    const url = `${base}redirect/chain/20`\n    return assert.rejects(fetch(url), new TypeError('fetch failed'))\n  })\n\n  it('should obey redirect chain, resolve case', () => {\n    const url = `${base}redirect/chain/19`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should support redirect mode, error flag', () => {\n    const url = `${base}redirect/301`\n    const options = {\n      redirect: 'error'\n    }\n    return assert.rejects(fetch(url, options), new TypeError('fetch failed'))\n  })\n\n  it('should support redirect mode, manual flag when there is no redirect', () => {\n    const url = `${base}hello`\n    const options = {\n      redirect: 'manual'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.headers.get('location'), null)\n    })\n  })\n\n  it('should follow redirect code 301 and keep existing headers', () => {\n    const url = `${base}redirect/301`\n    const options = {\n      headers: new Headers({ 'x-custom-header': 'abc' })\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, `${base}inspect`)\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.headers['x-custom-header'], 'abc')\n    })\n  })\n\n  it('should treat broken redirect as ordinary response (follow)', () => {\n    const url = `${base}redirect/no-location`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.status, 301)\n      assert.strictEqual(res.headers.get('location'), null)\n    })\n  })\n\n  it('should treat broken redirect as ordinary response (manual)', () => {\n    const url = `${base}redirect/no-location`\n    const options = {\n      redirect: 'manual'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.status, 301)\n      assert.strictEqual(res.headers.get('location'), null)\n    })\n  })\n\n  it('should throw a TypeError on an invalid redirect option', () => {\n    const url = `${base}redirect/301`\n    const options = {\n      redirect: 'foobar'\n    }\n    return fetch(url, options).then(() => {\n      assert.fail()\n    }, error => {\n      assert.ok(error instanceof TypeError)\n    })\n  })\n\n  it('should set redirected property on response when redirect', () => {\n    const url = `${base}redirect/301`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.redirected, true)\n    })\n  })\n\n  it('should not set redirected property on response without redirect', () => {\n    const url = `${base}hello`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.redirected, false)\n    })\n  })\n\n  it('should handle client-error response', () => {\n    const url = `${base}error/400`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      assert.strictEqual(res.status, 400)\n      assert.strictEqual(res.statusText, 'Bad Request')\n      assert.strictEqual(res.ok, false)\n      return res.text().then(result => {\n        assert.strictEqual(res.bodyUsed, true)\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'client error')\n      })\n    })\n  })\n\n  it('should handle server-error response', () => {\n    const url = `${base}error/500`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      assert.strictEqual(res.status, 500)\n      assert.strictEqual(res.statusText, 'Internal Server Error')\n      assert.strictEqual(res.ok, false)\n      return res.text().then(result => {\n        assert.strictEqual(res.bodyUsed, true)\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'server error')\n      })\n    })\n  })\n\n  it('should handle network-error response', () => {\n    const url = `${base}error/reset`\n    return assert.rejects(fetch(url), new TypeError('fetch failed'))\n  })\n\n  it('should handle network-error partial response', () => {\n    const url = `${base}error/premature`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.ok, true)\n      return assert.rejects(() => res.text(), new TypeError('terminated'))\n    })\n  })\n\n  it('should handle network-error in chunked response async iterator', () => {\n    const url = `${base}error/premature/chunked`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.ok, true)\n\n      const read = async body => {\n        const chunks = []\n        for await (const chunk of body) {\n          chunks.push(chunk)\n        }\n\n        return chunks\n      }\n\n      return assert.rejects(read(res.body), new TypeError('terminated'))\n    })\n  })\n\n  it('should handle network-error in chunked response in consumeBody', () => {\n    const url = `${base}error/premature/chunked`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.ok, true)\n\n      return assert.rejects(res.text(), new TypeError('terminated'))\n    })\n  })\n\n  it('should handle DNS-error response', () => {\n    const url = 'http://domain.invalid'\n    return assert.rejects(fetch(url), new TypeError('fetch failed'))\n  })\n\n  // TODO: Should we pass through the error message?\n  it('should reject invalid json response', () => {\n    const url = `${base}error/json`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'application/json')\n      return assert.rejects(res.json(), SyntaxError)\n    })\n  })\n\n  it('should handle response with no status text', () => {\n    const url = `${base}no-status-text`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.statusText, '')\n    })\n  })\n\n  it('should handle no content response', () => {\n    const url = `${base}no-content`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 204)\n      assert.strictEqual(res.statusText, 'No Content')\n      assert.strictEqual(res.ok, true)\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '')\n      })\n    })\n  })\n\n  // TODO: Should we pass through the error message?\n  it('should reject when trying to parse no content response as json', () => {\n    const url = `${base}no-content`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 204)\n      assert.strictEqual(res.statusText, 'No Content')\n      assert.strictEqual(res.ok, true)\n      return assert.rejects(res.json(), new SyntaxError('Unexpected end of JSON input'))\n    })\n  })\n\n  it('should handle no content response with gzip encoding', () => {\n    const url = `${base}no-content/gzip`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 204)\n      assert.strictEqual(res.statusText, 'No Content')\n      assert.strictEqual(res.headers.get('content-encoding'), 'gzip')\n      assert.strictEqual(res.ok, true)\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '')\n      })\n    })\n  })\n\n  it('should handle not modified response', () => {\n    const url = `${base}not-modified`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 304)\n      assert.strictEqual(res.statusText, 'Not Modified')\n      assert.strictEqual(res.ok, false)\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '')\n      })\n    })\n  })\n\n  it('should handle not modified response with gzip encoding', () => {\n    const url = `${base}not-modified/gzip`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 304)\n      assert.strictEqual(res.statusText, 'Not Modified')\n      assert.strictEqual(res.headers.get('content-encoding'), 'gzip')\n      assert.strictEqual(res.ok, false)\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '')\n      })\n    })\n  })\n\n  it('should decompress gzip response', () => {\n    const url = `${base}gzip`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'hello world')\n      })\n    })\n  })\n\n  it('should decompress slightly invalid gzip response', async () => {\n    const url = `${base}gzip-truncated`\n    const res = await fetch(url)\n    assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n    const result = await res.text()\n    assert.strictEqual(typeof result, 'string')\n    assert.strictEqual(result, 'hello world')\n  })\n\n  it('should decompress deflate response', () => {\n    const url = `${base}deflate`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'hello world')\n      })\n    })\n  })\n\n  it('should decompress deflate raw response from old apache server', () => {\n    const url = `${base}deflate-raw`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'hello world')\n      })\n    })\n  })\n\n  it('should decompress brotli response', function () {\n    if (typeof zlib.createBrotliDecompress !== 'function') {\n      this.skip()\n    }\n\n    const url = `${base}brotli`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'hello world')\n      })\n    })\n  })\n\n  it('should handle no content response with brotli encoding', function () {\n    if (typeof zlib.createBrotliDecompress !== 'function') {\n      this.skip()\n    }\n\n    const url = `${base}no-content/brotli`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.status, 204)\n      assert.strictEqual(res.statusText, 'No Content')\n      assert.strictEqual(res.headers.get('content-encoding'), 'br')\n      assert.strictEqual(res.ok, true)\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, '')\n      })\n    })\n  })\n\n  it('should skip decompression if unsupported', () => {\n    const url = `${base}sdch`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'fake sdch string')\n      })\n    })\n  })\n\n  it('should skip decompression if unsupported codings', () => {\n    const url = `${base}multiunsupported`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'multiunsupported')\n      })\n    })\n  })\n\n  it('should decompress multiple coding', () => {\n    const url = `${base}multisupported`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(result => {\n        assert.strictEqual(typeof result, 'string')\n        assert.strictEqual(result, 'hello world')\n      })\n    })\n  })\n\n  it('should reject if response compression is invalid', () => {\n    const url = `${base}invalid-content-encoding`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return assert.rejects(res.text(), new TypeError('terminated'))\n    })\n  })\n\n  it('should handle errors on the body stream even if it is not used', done => {\n    const url = `${base}invalid-content-encoding`\n    fetch(url)\n      .then(res => {\n        assert.strictEqual(res.status, 200)\n      })\n      .catch(() => { })\n      .then(new Promise((resolve) => {\n        // Wait a few ms to see if an uncaught error occurs\n        setTimeout(() => {\n          resolve()\n        }, 20)\n      }))\n  })\n\n  it('should collect handled errors on the body stream to reject if the body is used later', () => {\n    const url = `${base}invalid-content-encoding`\n    return fetch(url).then(delay(20)).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return assert.rejects(res.text(), new TypeError('terminated'))\n    })\n  })\n\n  it('should not overwrite existing accept-encoding header when auto decompression is true', () => {\n    const url = `${base}inspect`\n    const options = {\n      compress: true,\n      headers: {\n        'Accept-Encoding': 'gzip'\n      }\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.headers['accept-encoding'], 'gzip')\n    })\n  })\n\n  describe('AbortController', () => {\n    let controller\n\n    beforeEach(() => {\n      controller = new AbortController()\n    })\n\n    it('should support request cancellation with signal', () => {\n      const fetches = [\n        fetch(\n          `${base}timeout`,\n          {\n            method: 'POST',\n            signal: controller.signal,\n            headers: {\n              'Content-Type': 'application/json',\n              body: JSON.stringify({ hello: 'world' })\n            }\n          }\n        )\n      ]\n\n      controller.abort()\n\n      return Promise.all(fetches.map(async fetched => {\n        try {\n          await fetched\n          assert.fail('should have thrown')\n        } catch (error) {\n          assert.ok(error instanceof Error)\n          assert.strictEqual(error.name, 'AbortError')\n        }\n      }))\n    })\n\n    it('should support multiple request cancellation with signal', () => {\n      const fetches = [\n        fetch(`${base}timeout`, { signal: controller.signal }),\n        fetch(\n          `${base}timeout`,\n          {\n            method: 'POST',\n            signal: controller.signal,\n            headers: {\n              'Content-Type': 'application/json',\n              body: JSON.stringify({ hello: 'world' })\n            }\n          }\n        )\n      ]\n\n      controller.abort()\n\n      return Promise.all(fetches.map(async fetched => {\n        try {\n          await fetched\n          assert.fail('should have thrown')\n        } catch (error) {\n          assert.ok(error instanceof Error)\n          assert.strictEqual(error.name, 'AbortError')\n        }\n      }))\n    })\n\n    it('should reject immediately if signal has already been aborted', async () => {\n      const url = `${base}timeout`\n      const options = {\n        signal: controller.signal\n      }\n      controller.abort()\n      const fetched = fetch(url, options)\n\n      try {\n        await fetched\n        assert.fail('should have thrown')\n      } catch (error) {\n        assert.ok(error instanceof Error)\n        assert.strictEqual(error.name, 'AbortError')\n      }\n    })\n\n    it('should allow redirects to be aborted', async () => {\n      const request = new Request(`${base}redirect/slow`, {\n        signal: controller.signal\n      })\n      setTimeout(() => {\n        controller.abort()\n      }, 20)\n\n      try {\n        await fetch(request)\n        assert.fail('should have thrown')\n      } catch (error) {\n        assert.ok(error instanceof Error)\n        assert.strictEqual(error.name, 'AbortError')\n      }\n    })\n\n    it('should allow redirected response body to be aborted', async () => {\n      const request = new Request(`${base}redirect/slow-stream`, {\n        signal: controller.signal\n      })\n      const fetched = fetch(request).then(res => {\n        assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n        const result = res.text()\n        controller.abort()\n        return result\n      })\n\n      try {\n        await fetched\n        assert.fail('should have thrown')\n      } catch (error) {\n        assert.ok(error instanceof Error)\n        assert.strictEqual(error.name, 'AbortError')\n      }\n    })\n\n    it('should reject response body with AbortError when aborted before stream has been read completely', async () => {\n      const response = await fetch(\n        `${base}slow`,\n        { signal: controller.signal }\n      )\n\n      const promise = response.text()\n      controller.abort()\n\n      try {\n        await promise\n        assert.fail('should have thrown')\n      } catch (error) {\n        assert.ok(error instanceof Error)\n        assert.strictEqual(error.name, 'AbortError')\n      }\n    })\n\n    it('should reject response body methods immediately with AbortError when aborted before stream is disturbed', async () => {\n      const response = await fetch(\n        `${base}slow`,\n        { signal: controller.signal }\n      )\n\n      controller.abort()\n      const promise = response.text()\n      try {\n        await promise\n        assert.fail('should have thrown')\n      } catch (error) {\n        assert.ok(error instanceof Error)\n        assert.strictEqual(error.name, 'AbortError')\n      }\n    })\n  })\n\n  it('should throw a TypeError if a signal is not of type AbortSignal or EventTarget', () => {\n    return Promise.all([\n      assert.rejects(fetch(`${base}inspect`, { signal: {} }), new TypeError('RequestInit: Expected signal (\"{}\") to be an instance of AbortSignal.')),\n      assert.rejects(fetch(`${base}inspect`, { signal: '' }), new TypeError('RequestInit: Expected signal (\"\"\"\") to be an instance of AbortSignal.')),\n      assert.rejects(fetch(`${base}inspect`, { signal: Object.create(null) }), new TypeError('RequestInit: Expected signal (\"[Object: null prototype] {}\") to be an instance of AbortSignal.'))\n    ])\n  })\n\n  it('should gracefully handle a null signal', () => {\n    return fetch(`${base}hello`, { signal: null }).then(res => {\n      return assert.strictEqual(res.ok, true)\n    })\n  })\n\n  it('should allow setting User-Agent', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: {\n        'user-agent': 'faked'\n      }\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.headers['user-agent'], 'faked')\n    })\n  })\n\n  it('should set default Accept header', () => {\n    const url = `${base}inspect`\n    fetch(url).then(res => res.json()).then(res => {\n      assert.strictEqual(res.headers.accept, '*/*')\n    })\n  })\n\n  it('should allow setting Accept header', () => {\n    const url = `${base}inspect`\n    const options = {\n      headers: {\n        accept: 'application/json'\n      }\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.headers.accept, 'application/json')\n    })\n  })\n\n  it('should allow POST request', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '0')\n    })\n  })\n\n  it('should allow POST request with string body', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], 'text/plain;charset=UTF-8')\n      assert.strictEqual(res.headers['content-length'], '3')\n    })\n  })\n\n  it('should allow POST request with buffer body', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: Buffer.from('a=1', 'utf-8')\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '3')\n    })\n  })\n\n  it('should allow POST request with ArrayBuffer body', () => {\n    const encoder = new TextEncoder()\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: encoder.encode('Hello, world!\\n').buffer\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'Hello, world!\\n')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '14')\n    })\n  })\n\n  it('should allow POST request with ArrayBuffer body from a VM context', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new VMUint8Array(Buffer.from('Hello, world!\\n')).buffer\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'Hello, world!\\n')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '14')\n    })\n  })\n\n  it('should allow POST request with ArrayBufferView (Uint8Array) body', () => {\n    const encoder = new TextEncoder()\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: encoder.encode('Hello, world!\\n')\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'Hello, world!\\n')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '14')\n    })\n  })\n\n  it('should allow POST request with ArrayBufferView (BigUint64Array) body', () => {\n    const encoder = new TextEncoder()\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new BigUint64Array(encoder.encode('0123456789abcdef').buffer)\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, '0123456789abcdef')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '16')\n    })\n  })\n\n  it('should allow POST request with ArrayBufferView (DataView) body', () => {\n    const encoder = new TextEncoder()\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new DataView(encoder.encode('Hello, world!\\n').buffer)\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'Hello, world!\\n')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '14')\n    })\n  })\n\n  it('should allow POST request with ArrayBufferView (Uint8Array) body from a VM context', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new VMUint8Array(Buffer.from('Hello, world!\\n'))\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'Hello, world!\\n')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '14')\n    })\n  })\n\n  it('should allow POST request with ArrayBufferView (Uint8Array, offset, length) body', () => {\n    const encoder = new TextEncoder()\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: encoder.encode('Hello, world!\\n').subarray(7, 13)\n    }\n    return fetch(url, options).then(res => res.json()).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'world!')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '6')\n    })\n  })\n\n  it('should allow POST request with blob body without type', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new Blob(['a=1'])\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      // assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], '3')\n    })\n  })\n\n  it('should allow POST request with blob body with type', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: new Blob(['a=1'], {\n        type: 'text/plain;charset=UTF-8'\n      })\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-type'], 'text/plain;charset=utf-8')\n      assert.strictEqual(res.headers['content-length'], '3')\n    })\n  })\n\n  it('should allow POST request with readable stream as body', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: stream.Readable.from('a=1'),\n      duplex: 'half'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], 'chunked')\n      assert.strictEqual(res.headers['content-type'], undefined)\n      assert.strictEqual(res.headers['content-length'], undefined)\n    })\n  })\n\n  it('should allow POST request with object body', () => {\n    const url = `${base}inspect`\n    // Note that fetch simply calls tostring on an object\n    const options = {\n      method: 'POST',\n      body: { a: 1 }\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.body, '[object Object]')\n      assert.strictEqual(res.headers['content-type'], 'text/plain;charset=UTF-8')\n      assert.strictEqual(res.headers['content-length'], '15')\n    })\n  })\n\n  it('should allow POST request with form-data as body', () => {\n    const form = new FormData()\n    form.append('a', '1')\n\n    const url = `${base}multipart`\n    const options = {\n      method: 'POST',\n      body: form\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.ok(res.headers['content-type'].startsWith('multipart/form-data; boundary='))\n      assert.strictEqual(res.body, 'a=1')\n    })\n  })\n\n  it('constructing a Response with URLSearchParams as body should have a Content-Type', () => {\n    const parameters = new URLSearchParams()\n    const res = new Response(parameters)\n    res.headers.get('Content-Type')\n    assert.strictEqual(res.headers.get('Content-Type'), 'application/x-www-form-urlencoded;charset=UTF-8')\n  })\n\n  it('constructing a Request with URLSearchParams as body should have a Content-Type', () => {\n    const parameters = new URLSearchParams()\n    const request = new Request(base, { method: 'POST', body: parameters })\n    assert.strictEqual(request.headers.get('Content-Type'), 'application/x-www-form-urlencoded;charset=UTF-8')\n  })\n\n  it('Reading a body with URLSearchParams should echo back the result', () => {\n    const parameters = new URLSearchParams()\n    parameters.append('a', '1')\n    return new Response(parameters).text().then(text => {\n      assert.strictEqual(text, 'a=1')\n    })\n  })\n\n  // Body should be cloned...\n  it('constructing a Request/Response with URLSearchParams and mutating it should not affected body', () => {\n    const parameters = new URLSearchParams()\n    const request = new Request(`${base}inspect`, { method: 'POST', body: parameters })\n    parameters.append('a', '1')\n    return request.text().then(text => {\n      assert.strictEqual(text, '')\n    })\n  })\n\n  it('should allow POST request with URLSearchParams as body', () => {\n    const parameters = new URLSearchParams()\n    parameters.append('a', '1')\n\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: parameters\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.headers['content-type'], 'application/x-www-form-urlencoded;charset=UTF-8')\n      assert.strictEqual(res.headers['content-length'], '3')\n      assert.strictEqual(res.body, 'a=1')\n    })\n  })\n\n  it('should still recognize URLSearchParams when extended', () => {\n    class CustomSearchParameters extends URLSearchParams { }\n    const parameters = new CustomSearchParameters()\n    parameters.append('a', '1')\n\n    const url = `${base}inspect`\n    const options = {\n      method: 'POST',\n      body: parameters\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'POST')\n      assert.strictEqual(res.headers['content-type'], 'application/x-www-form-urlencoded;charset=UTF-8')\n      assert.strictEqual(res.headers['content-length'], '3')\n      assert.strictEqual(res.body, 'a=1')\n    })\n  })\n\n  it('should allow PUT request', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'PUT',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'PUT')\n      assert.strictEqual(res.body, 'a=1')\n    })\n  })\n\n  it('should allow DELETE request', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'DELETE'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'DELETE')\n    })\n  })\n\n  it('should allow DELETE request with string body', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'DELETE',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'DELETE')\n      assert.strictEqual(res.body, 'a=1')\n      assert.strictEqual(res.headers['transfer-encoding'], undefined)\n      assert.strictEqual(res.headers['content-length'], '3')\n    })\n  })\n\n  it('should allow PATCH request', () => {\n    const url = `${base}inspect`\n    const options = {\n      method: 'PATCH',\n      body: 'a=1'\n    }\n    return fetch(url, options).then(res => {\n      return res.json()\n    }).then(res => {\n      assert.strictEqual(res.method, 'PATCH')\n      assert.strictEqual(res.body, 'a=1')\n    })\n  })\n\n  it('should allow HEAD request', () => {\n    const url = `${base}hello`\n    const options = {\n      method: 'HEAD'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.statusText, 'OK')\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      // assert.ok(res.body instanceof stream.Transform)\n      return res.text()\n    }).then(text => {\n      assert.strictEqual(text, '')\n    })\n  })\n\n  it('should allow HEAD request with content-encoding header', () => {\n    const url = `${base}error/404`\n    const options = {\n      method: 'HEAD'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.status, 404)\n      assert.strictEqual(res.headers.get('content-encoding'), 'gzip')\n      return res.text()\n    }).then(text => {\n      assert.strictEqual(text, '')\n    })\n  })\n\n  it('should allow OPTIONS request', () => {\n    const url = `${base}options`\n    const options = {\n      method: 'OPTIONS'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.status, 200)\n      assert.strictEqual(res.statusText, 'OK')\n      assert.strictEqual(res.headers.get('allow'), 'GET, HEAD, OPTIONS')\n      // assert.ok(res.body instanceof stream.Transform)\n    })\n  })\n\n  it('should reject decoding body twice', () => {\n    const url = `${base}plain`\n    return fetch(url).then(res => {\n      assert.strictEqual(res.headers.get('content-type'), 'text/plain')\n      return res.text().then(() => {\n        assert.strictEqual(res.bodyUsed, true)\n        return assert.rejects(res.text(), new TypeError('Body is unusable: Body has already been read'))\n      })\n    })\n  })\n\n  it('should allow cloning a json response and log it as text response', () => {\n    const url = `${base}json`\n    return fetch(url).then(res => {\n      const r1 = res.clone()\n      return Promise.all([res.json(), r1.text()]).then(results => {\n        assert.deepStrictEqual(results[0], { name: 'value' })\n        assert.strictEqual(results[1], '{\"name\":\"value\"}')\n      })\n    })\n  })\n\n  it('should allow cloning a json response, and then log it as text response', () => {\n    const url = `${base}json`\n    return fetch(url).then(res => {\n      const r1 = res.clone()\n      return res.json().then(result => {\n        assert.deepStrictEqual(result, { name: 'value' })\n        return r1.text().then(result => {\n          assert.strictEqual(result, '{\"name\":\"value\"}')\n        })\n      })\n    })\n  })\n\n  it('should allow cloning a json response, first log as text response, then return json object', () => {\n    const url = `${base}json`\n    return fetch(url).then(res => {\n      const r1 = res.clone()\n      return r1.text().then(result => {\n        assert.strictEqual(result, '{\"name\":\"value\"}')\n        return res.json().then(result => {\n          assert.deepStrictEqual(result, { name: 'value' })\n        })\n      })\n    })\n  })\n\n  it('should not allow cloning a response after its been used', () => {\n    const url = `${base}hello`\n    return fetch(url).then(res =>\n      res.text().then(() => {\n        assert.throws(() => {\n          res.clone()\n        }, new TypeError('Response.clone: Body has already been consumed.'))\n      })\n    )\n  })\n\n  /* global expect */\n\n  // TODO: fix test.\n  it.skip('should timeout on cloning response without consuming one of the streams when the second packet size is equal default highWaterMark', { timeout: 300 }, function () {\n    const url = local.mockState(res => {\n      // Observed behavior of TCP packets splitting:\n      // - response body size <= 65438 → single packet sent\n      // - response body size  > 65438 → multiple packets sent\n      // Max TCP packet size is 64kB (http://stackoverflow.com/a/2614188/5763764),\n      // but first packet probably transfers more than the response body.\n      const firstPacketMaxSize = 65438\n      const secondPacketSize = 16 * 1024 // = defaultHighWaterMark\n      res.end(crypto.randomBytes(firstPacketMaxSize + secondPacketSize))\n    })\n    return expect(\n      fetch(url).then(res => res.clone().buffer())\n    ).to.timeout\n  })\n\n  // TODO: fix test.\n  it.skip('should timeout on cloning response without consuming one of the streams when the second packet size is equal custom highWaterMark', { timeout: 300 }, function () {\n    const url = local.mockState(res => {\n      const firstPacketMaxSize = 65438\n      const secondPacketSize = 10\n      res.end(crypto.randomBytes(firstPacketMaxSize + secondPacketSize))\n    })\n    return expect(\n      fetch(url, { highWaterMark: 10 }).then(res => res.clone().buffer())\n    ).to.timeout\n  })\n\n  // TODO: fix test.\n  it.skip('should not timeout on cloning response without consuming one of the streams when the second packet size is less than default highWaterMark', { timeout: 300 }, async function () {\n    const url = local.mockState(res => {\n      const firstPacketMaxSize = 65438\n      const secondPacketSize = 16 * 1024 // = defaultHighWaterMark\n      res.end(crypto.randomBytes(firstPacketMaxSize + secondPacketSize - 1))\n    })\n    return expect(\n      fetch(url).then(res => res.clone().buffer())\n    ).not.to.timeout\n  })\n\n  // TODO: fix test.\n  it.skip('should not timeout on cloning response without consuming one of the streams when the second packet size is less than custom highWaterMark', { timeout: 300 }, function () {\n    const url = local.mockState(res => {\n      const firstPacketMaxSize = 65438\n      const secondPacketSize = 10\n      res.end(crypto.randomBytes(firstPacketMaxSize + secondPacketSize - 1))\n    })\n    return expect(\n      fetch(url, { highWaterMark: 10 }).then(res => res.clone().buffer())\n    ).not.to.timeout\n  })\n\n  // TODO: fix test.\n  it.skip('should not timeout on cloning response without consuming one of the streams when the response size is double the custom large highWaterMark - 1', { timeout: 300 }, function () {\n    const url = local.mockState(res => {\n      res.end(crypto.randomBytes((2 * 512 * 1024) - 1))\n    })\n    return expect(\n      fetch(url, { highWaterMark: 512 * 1024 }).then(res => res.clone().buffer())\n    ).not.to.timeout\n  })\n\n  // TODO: fix test.\n  it.skip('should allow get all responses of a header', () => {\n    const url = `${base}cookie`\n    return fetch(url).then(res => {\n      const expected = 'a=1, b=1'\n      assert.strictEqual(res.headers.get('set-cookie'), expected)\n      assert.strictEqual(res.headers.get('Set-Cookie'), expected)\n    })\n  })\n\n  it('should support fetch with Request instance', () => {\n    const url = `${base}hello`\n    const request = new Request(url)\n    return fetch(request).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.ok, true)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should support fetch with Node.js URL object', () => {\n    const url = `${base}hello`\n    const urlObject = new URL(url)\n    const request = new Request(urlObject)\n    return fetch(request).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.ok, true)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should support fetch with WHATWG URL object', () => {\n    const url = `${base}hello`\n    const urlObject = new URL(url)\n    const request = new Request(urlObject)\n    return fetch(request).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.ok, true)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('if params are given, do not modify anything', () => {\n    const url = `${base}question?a=1`\n    const urlObject = new URL(url)\n    const request = new Request(urlObject)\n    return fetch(request).then(res => {\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.ok, true)\n      assert.strictEqual(res.status, 200)\n    })\n  })\n\n  it('should support reading blob as text', () => {\n    return new Response('hello')\n      .blob()\n      .then(blob => blob.text())\n      .then(body => {\n        assert.strictEqual(body, 'hello')\n      })\n  })\n\n  it('should support reading blob as arrayBuffer', () => {\n    return new Response('hello')\n      .blob()\n      .then(blob => blob.arrayBuffer())\n      .then(ab => {\n        const string = String.fromCharCode.apply(null, new Uint8Array(ab))\n        assert.strictEqual(string, 'hello')\n      })\n  })\n\n  it('should support blob round-trip', () => {\n    const url = `${base}hello`\n\n    let length\n    let type\n\n    return fetch(url).then(res => res.blob()).then(async blob => {\n      const url = `${base}inspect`\n      length = blob.size\n      type = blob.type\n      return fetch(url, {\n        method: 'POST',\n        body: blob\n      })\n    }).then(res => res.json()).then(({ body, headers }) => {\n      assert.strictEqual(body, 'world')\n      assert.strictEqual(headers['content-type'], type)\n      assert.strictEqual(headers['content-length'], String(length))\n    })\n  })\n\n  it('should support overwrite Request instance', () => {\n    const url = `${base}inspect`\n    const request = new Request(url, {\n      method: 'POST',\n      headers: {\n        a: '1'\n      }\n    })\n    return fetch(request, {\n      method: 'GET',\n      headers: {\n        a: '2'\n      }\n    }).then(res => {\n      return res.json()\n    }).then(body => {\n      assert.strictEqual(body.method, 'GET')\n      assert.strictEqual(body.headers.a, '2')\n    })\n  })\n\n  it('should support http request', { timeout: 5000 }, async function (t) {\n    t = tspl(t, { plan: 2 })\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end()\n    })\n    after(() => server.close())\n    server.listen(0, () => {\n      const url = `http://localhost:${server.address().port}`\n      const options = {\n        method: 'HEAD'\n      }\n      fetch(url, options).then(res => {\n        t.strictEqual(res.status, 200)\n        t.strictEqual(res.ok, true)\n      })\n    })\n    await t.completed\n  })\n\n  it('should encode URLs as UTF-8', async () => {\n    const url = `${base}möbius`\n    const res = await fetch(url)\n    assert.strictEqual(res.url, `${base}m%C3%B6bius`)\n  })\n\n  it('should allow manual redirect handling', { timeout: 5000 }, function () {\n    const url = `${base}redirect/302`\n    const options = {\n      redirect: 'manual'\n    }\n    return fetch(url, options).then(res => {\n      assert.strictEqual(res.status, 302)\n      assert.strictEqual(res.url, url)\n      assert.strictEqual(res.type, 'basic')\n      assert.strictEqual(res.headers.get('Location'), '/inspect')\n      assert.strictEqual(res.ok, false)\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/mock.js",
    "content": "'use strict'\n\n// Test tools\nconst assert = require('node:assert')\nconst { describe, it } = require('node:test')\n\nconst {\n  fetch,\n  MockAgent,\n  setGlobalDispatcher,\n  Headers\n} = require('../../index.js')\n\ndescribe('node-fetch with MockAgent', () => {\n  it('should match the url', async () => {\n    const mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    const mockPool = mockAgent.get('http://localhost:3000')\n\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET'\n      })\n      .reply(200, { success: true })\n      .persist()\n\n    const res = await fetch('http://localhost:3000/test', {\n      method: 'GET'\n    })\n\n    assert.strictEqual(res.status, 200)\n    assert.deepStrictEqual(await res.json(), { success: true })\n  })\n\n  it('should match the body', async () => {\n    const mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    const mockPool = mockAgent.get('http://localhost:3000')\n\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'POST',\n        body: (value) => {\n          return value === 'request body'\n        }\n      })\n      .reply(200, { success: true })\n      .persist()\n\n    const res = await fetch('http://localhost:3000/test', {\n      method: 'POST',\n      body: 'request body'\n    })\n\n    assert.strictEqual(res.status, 200)\n    assert.deepStrictEqual(await res.json(), { success: true })\n  })\n\n  it('should match the headers', async () => {\n    const mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    const mockPool = mockAgent.get('http://localhost:3000')\n\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET',\n        headers: (h) => {\n          return h['user-agent'] === 'undici'\n        }\n      })\n      .reply(200, { success: true })\n      .persist()\n\n    const res = await fetch('http://localhost:3000/test', {\n      method: 'GET',\n      headers: new Headers({ 'User-Agent': 'undici' })\n    })\n\n    assert.strictEqual(res.status, 200)\n    assert.deepStrictEqual(await res.json(), { success: true })\n  })\n\n  it('should match the headers with a matching function', async () => {\n    const mockAgent = new MockAgent()\n    setGlobalDispatcher(mockAgent)\n    const mockPool = mockAgent.get('http://localhost:3000')\n\n    mockPool\n      .intercept({\n        path: '/test',\n        method: 'GET',\n        headers (headers) {\n          assert.strictEqual(typeof headers, 'object')\n          assert.strictEqual(headers['user-agent'], 'undici')\n          return true\n        }\n      })\n      .reply(200, { success: true })\n      .persist()\n\n    const res = await fetch('http://localhost:3000/test', {\n      method: 'GET',\n      headers: new Headers({ 'User-Agent': 'undici' })\n    })\n\n    assert.strictEqual(res.status, 200)\n    assert.deepStrictEqual(await res.json(), { success: true })\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/request.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { describe, it, before, after } = require('node:test')\nconst stream = require('node:stream')\nconst http = require('node:http')\n\nconst { Request, FormData } = require('../../index.js')\nconst TestServer = require('./utils/server.js')\n\ndescribe('Request', () => {\n  const local = new TestServer()\n  let base\n\n  before(async () => {\n    await local.start()\n    base = `http://${local.hostname}:${local.port}/`\n  })\n\n  after(async () => {\n    return local.stop()\n  })\n\n  it('should have attributes conforming to Web IDL', () => {\n    const request = new Request('http://github.com/')\n    const enumerableProperties = []\n    for (const property in request) {\n      enumerableProperties.push(property)\n    }\n\n    for (const toCheck of [\n      'body',\n      'bodyUsed',\n      'arrayBuffer',\n      'blob',\n      'json',\n      'text',\n      'method',\n      'url',\n      'headers',\n      'redirect',\n      'clone',\n      'signal'\n    ]) {\n      assert.ok(enumerableProperties.includes(toCheck))\n    }\n\n    for (const toCheck of [\n      'body', 'bodyUsed', 'method', 'url', 'headers', 'redirect', 'signal'\n    ]) {\n      assert.throws(() => {\n        request[toCheck] = 'abc'\n      }, new TypeError(`Cannot set property ${toCheck} of #<Request> which has only a getter`))\n    }\n  })\n\n  it.skip('should support wrapping Request instance', () => {\n    const url = `${base}hello`\n\n    const form = new FormData()\n    form.append('a', '1')\n    const { signal } = new AbortController()\n\n    const r1 = new Request(url, {\n      method: 'POST',\n      follow: 1,\n      body: form,\n      signal\n    })\n    const r2 = new Request(r1, {\n      follow: 2\n    })\n\n    assert.strictEqual(r2.url, url)\n    assert.strictEqual(r2.method, 'POST')\n    assert.strictEqual(r2.signal[Symbol.toStringTag], 'AbortSignal')\n    // Note that we didn't clone the body\n    assert.strictEqual(r2.body, form)\n    assert.strictEqual(r1.follow, 1)\n    assert.strictEqual(r2.follow, 2)\n    assert.strictEqual(r1.counter, 0)\n    assert.strictEqual(r2.counter, 0)\n  })\n\n  it.skip('should override signal on derived Request instances', () => {\n    const parentAbortController = new AbortController()\n    const derivedAbortController = new AbortController()\n    const parentRequest = new Request(`${base}hello`, {\n      signal: parentAbortController.signal\n    })\n    const derivedRequest = new Request(parentRequest, {\n      signal: derivedAbortController.signal\n    })\n    assert.strictEqual(parentRequest.signal, parentAbortController.signal)\n    assert.strictEqual(derivedRequest.signal, derivedAbortController.signal)\n  })\n\n  it.skip('should allow removing signal on derived Request instances', () => {\n    const parentAbortController = new AbortController()\n    const parentRequest = new Request(`${base}hello`, {\n      signal: parentAbortController.signal\n    })\n    const derivedRequest = new Request(parentRequest, {\n      signal: null\n    })\n    assert.strictEqual(parentRequest.signal, parentAbortController.signal)\n    assert.strictEqual(derivedRequest.signal, null)\n  })\n\n  it('should throw error with GET/HEAD requests with body', () => {\n    assert.throws(() => new Request(base, { body: '' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: 'a' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: '', method: 'HEAD' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: 'a', method: 'HEAD' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: '', method: 'get' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: 'a', method: 'get' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: '', method: 'head' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n    assert.throws(() => new Request(base, { body: 'a', method: 'head' }), new TypeError('Request with GET/HEAD method cannot have body.'))\n  })\n\n  it('should default to null as body', () => {\n    const request = new Request(base)\n    assert.strictEqual(request.body, null)\n    return request.text().then(result => assert.strictEqual(result, ''))\n  })\n\n  it('should support parsing headers', () => {\n    const url = base\n    const request = new Request(url, {\n      headers: {\n        a: '1'\n      }\n    })\n    assert.strictEqual(request.url, url)\n    assert.strictEqual(request.headers.get('a'), '1')\n  })\n\n  it('should support arrayBuffer() method', () => {\n    const url = base\n    const request = new Request(url, {\n      method: 'POST',\n      body: 'a=1'\n    })\n    assert.strictEqual(request.url, url)\n    return request.arrayBuffer().then(result => {\n      assert.ok(result instanceof ArrayBuffer)\n      const string = String.fromCharCode.apply(null, new Uint8Array(result))\n      assert.strictEqual(string, 'a=1')\n    })\n  })\n\n  it('should support text() method', () => {\n    const url = base\n    const request = new Request(url, {\n      method: 'POST',\n      body: 'a=1'\n    })\n    assert.strictEqual(request.url, url)\n    return request.text().then(result => {\n      assert.strictEqual(result, 'a=1')\n    })\n  })\n\n  it('should support json() method', () => {\n    const url = base\n    const request = new Request(url, {\n      method: 'POST',\n      body: '{\"a\":1}'\n    })\n    assert.strictEqual(request.url, url)\n    return request.json().then(result => {\n      assert.strictEqual(result.a, 1)\n    })\n  })\n\n  it('should support blob() method', () => {\n    const url = base\n    const request = new Request(url, {\n      method: 'POST',\n      body: Buffer.from('a=1')\n    })\n    assert.strictEqual(request.url, url)\n    return request.blob().then(result => {\n      assert.ok(result instanceof Blob)\n      assert.strictEqual(result.size, 3)\n      assert.strictEqual(result.type, '')\n    })\n  })\n\n  it('should support clone() method', () => {\n    const url = base\n    const body = stream.Readable.from('a=1')\n    const agent = new http.Agent()\n    const { signal } = new AbortController()\n    const request = new Request(url, {\n      body,\n      method: 'POST',\n      redirect: 'manual',\n      headers: {\n        b: '2'\n      },\n      follow: 3,\n      compress: false,\n      agent,\n      signal,\n      duplex: 'half'\n    })\n    const cl = request.clone()\n    assert.strictEqual(cl.url, url)\n    assert.strictEqual(cl.method, 'POST')\n    assert.strictEqual(cl.redirect, 'manual')\n    assert.strictEqual(cl.headers.get('b'), '2')\n    assert.strictEqual(cl.method, 'POST')\n    // Clone body shouldn't be the same body\n    assert.notDeepEqual(cl.body, body)\n    return Promise.all([cl.text(), request.text()]).then(results => {\n      assert.strictEqual(results[0], 'a=1')\n      assert.strictEqual(results[1], 'a=1')\n    })\n  })\n\n  it('should support ArrayBuffer as body', () => {\n    const encoder = new TextEncoder()\n    const body = encoder.encode('a=12345678901234').buffer\n    const request = new Request(base, {\n      method: 'POST',\n      body\n    })\n    new Uint8Array(body)[0] = 0\n    return request.text().then(result => {\n      assert.strictEqual(result, 'a=12345678901234')\n    })\n  })\n\n  it('should support Uint8Array as body', () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new Uint8Array(fullbuffer, 2, 9)\n    const request = new Request(base, {\n      method: 'POST',\n      body\n    })\n    body[0] = 0\n    return request.text().then(result => {\n      assert.strictEqual(result, '123456789')\n    })\n  })\n\n  it('should support BigUint64Array as body', () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new BigUint64Array(fullbuffer, 8, 1)\n    const request = new Request(base, {\n      method: 'POST',\n      body\n    })\n    body[0] = 0n\n    return request.text().then(result => {\n      assert.strictEqual(result, '78901234')\n    })\n  })\n\n  it('should support DataView as body', () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new Uint8Array(fullbuffer, 2, 9)\n    const request = new Request(base, {\n      method: 'POST',\n      body\n    })\n    body[0] = 0\n    return request.text().then(result => {\n      assert.strictEqual(result, '123456789')\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/response.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { describe, it, before, after } = require('node:test')\nconst stream = require('node:stream')\nconst { Response } = require('../../index.js')\nconst TestServer = require('./utils/server.js')\n\ndescribe('Response', () => {\n  const local = new TestServer()\n\n  before(async () => {\n    await local.start()\n  })\n\n  after(async () => {\n    return local.stop()\n  })\n\n  it('should have attributes conforming to Web IDL', () => {\n    const res = new Response()\n    const enumerableProperties = []\n    for (const property in res) {\n      enumerableProperties.push(property)\n    }\n\n    for (const toCheck of [\n      'body',\n      'bodyUsed',\n      'arrayBuffer',\n      'blob',\n      'json',\n      'text',\n      'type',\n      'url',\n      'status',\n      'ok',\n      'redirected',\n      'statusText',\n      'headers',\n      'clone'\n    ]) {\n      assert.ok(enumerableProperties.includes(toCheck))\n    }\n\n    for (const toCheck of [\n      'body',\n      'bodyUsed',\n      'type',\n      'url',\n      'status',\n      'ok',\n      'redirected',\n      'statusText',\n      'headers'\n    ]) {\n      assert.throws(() => {\n        res[toCheck] = 'abc'\n      }, new TypeError(`Cannot set property ${toCheck} of #<Response> which has only a getter`))\n    }\n  })\n\n  it('should support empty options', async () => {\n    const res = new Response(stream.Readable.from('a=1'))\n    const result = await res.text()\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support parsing headers', () => {\n    const res = new Response(null, {\n      headers: {\n        a: '1'\n      }\n    })\n    assert.strictEqual(res.headers.get('a'), '1')\n  })\n\n  it('should support text() method', async () => {\n    const res = new Response('a=1')\n    const result = await res.text()\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support json() method', async () => {\n    const res = new Response('{\"a\":1}')\n    const result = await res.json()\n    assert.deepStrictEqual(result, { a: 1 })\n  })\n\n  if (Blob) {\n    it('should support blob() method', async () => {\n      const res = new Response('a=1', {\n        method: 'POST',\n        headers: {\n          'Content-Type': 'text/plain'\n        }\n      })\n      const result = await res.blob()\n      assert.ok(result instanceof Blob)\n      assert.strictEqual(result.size, 3)\n      assert.strictEqual(result.type, 'text/plain')\n    })\n  }\n\n  it('should support clone() method', () => {\n    const body = stream.Readable.from('a=1')\n    const res = new Response(body, {\n      headers: {\n        a: '1'\n      },\n      status: 346,\n      statusText: 'production'\n    })\n    const cl = res.clone()\n    assert.strictEqual(cl.headers.get('a'), '1')\n    assert.strictEqual(cl.type, 'default')\n    assert.strictEqual(cl.status, 346)\n    assert.strictEqual(cl.statusText, 'production')\n    assert.strictEqual(cl.ok, false)\n    // Clone body shouldn't be the same body\n    assert.notStrictEqual(cl.body, body)\n    return Promise.all([cl.text(), res.text()]).then(results => {\n      assert.strictEqual(results[0], 'a=1')\n      assert.strictEqual(results[1], 'a=1')\n    })\n  })\n\n  it('should support stream as body', async () => {\n    const body = stream.Readable.from('a=1')\n    const res = new Response(body)\n    const result = await res.text()\n\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support string as body', async () => {\n    const res = new Response('a=1')\n    const result = await res.text()\n\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support buffer as body', async () => {\n    const res = new Response(Buffer.from('a=1'))\n    const result = await res.text()\n\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support ArrayBuffer as body', async () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const res = new Response(fullbuffer)\n    new Uint8Array(fullbuffer)[0] = 0\n\n    const result = await res.text()\n    assert.strictEqual(result, 'a=12345678901234')\n  })\n\n  it('should support blob as body', async () => {\n    const res = new Response(new Blob(['a=1']))\n    const result = await res.text()\n\n    assert.strictEqual(result, 'a=1')\n  })\n\n  it('should support Uint8Array as body', async () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new Uint8Array(fullbuffer, 2, 9)\n    const res = new Response(body)\n    body[0] = 0\n\n    const result = await res.text()\n    assert.strictEqual(result, '123456789')\n  })\n\n  it('should support BigUint64Array as body', async () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new BigUint64Array(fullbuffer, 8, 1)\n    const res = new Response(body)\n    body[0] = 0n\n\n    const result = await res.text()\n    assert.strictEqual(result, '78901234')\n  })\n\n  it('should support DataView as body', async () => {\n    const encoder = new TextEncoder()\n    const fullbuffer = encoder.encode('a=12345678901234').buffer\n    const body = new Uint8Array(fullbuffer, 2, 9)\n    const res = new Response(body)\n    body[0] = 0\n\n    const result = await res.text()\n    assert.strictEqual(result, '123456789')\n  })\n\n  it('should default to null as body', () => {\n    const res = new Response()\n    assert.strictEqual(res.body, null)\n\n    return res.text().then(result => assert.strictEqual(result, ''))\n  })\n\n  it('should default to 200 as status code', () => {\n    const res = new Response(null)\n    assert.strictEqual(res.status, 200)\n  })\n\n  it('should default to empty string as url', () => {\n    const res = new Response()\n    assert.strictEqual(res.url, '')\n  })\n\n  it('should support error() static method', () => {\n    const res = Response.error()\n    assert.ok(res instanceof Response)\n    assert.strictEqual(res.status, 0)\n    assert.strictEqual(res.statusText, '')\n    assert.strictEqual(res.type, 'error')\n  })\n\n  it('should support undefined status', () => {\n    const res = new Response(null, { status: undefined })\n    assert.strictEqual(res.status, 200)\n  })\n\n  it('should support undefined statusText', () => {\n    const res = new Response(null, { statusText: undefined })\n    assert.strictEqual(res.statusText, '')\n  })\n\n  it('should not set bodyUsed to undefined', () => {\n    const res = new Response()\n    assert.strictEqual(res.bodyUsed, false)\n  })\n})\n"
  },
  {
    "path": "test/node-fetch/utils/dummy.txt",
    "content": "i am a dummy"
  },
  {
    "path": "test/node-fetch/utils/read-stream.js",
    "content": "module.exports = async function readStream (stream) {\n  const chunks = []\n\n  for await (const chunk of stream) {\n    chunks.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk))\n  }\n\n  return Buffer.concat(chunks)\n}\n"
  },
  {
    "path": "test/node-fetch/utils/server.js",
    "content": "'use strict'\n\nconst http = require('node:http')\nconst zlib = require('node:zlib')\nconst { once } = require('node:events')\nconst Busboy = require('@fastify/busboy')\n\nmodule.exports = class TestServer {\n  constructor () {\n    this.server = http.createServer({ joinDuplicateHeaders: true }, this.router)\n    // Node 8 default keepalive timeout is 5000ms\n    // make it shorter here as we want to close server quickly at the end of tests\n    this.server.keepAliveTimeout = 1000\n    this.server.on('error', err => {\n      console.log(err.stack)\n    })\n    this.server.on('connection', socket => {\n      socket.setTimeout(1500)\n    })\n  }\n\n  async start () {\n    this.server.listen(0, 'localhost')\n    return once(this.server, 'listening')\n  }\n\n  async stop () {\n    this.server.close()\n    return once(this.server, 'close')\n  }\n\n  get port () {\n    return this.server.address().port\n  }\n\n  get hostname () {\n    return 'localhost'\n  }\n\n  mockState (responseHandler) {\n    this.server.nextResponseHandler = responseHandler\n    return `http://${this.hostname}:${this.port}/mocked`\n  }\n\n  router (request, res) {\n    const p = request.url\n\n    if (p === '/mocked') {\n      if (this.nextResponseHandler) {\n        this.nextResponseHandler(res)\n        this.nextResponseHandler = undefined\n      } else {\n        throw new Error('No mocked response. Use ’TestServer.mockState()’.')\n      }\n    }\n\n    if (p === '/hello') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('world')\n    }\n\n    if (p.includes('question')) {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('ok')\n    }\n\n    if (p === '/plain') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('text')\n    }\n\n    if (p === '/no-status-text') {\n      res.writeHead(200, '', {}).end()\n    }\n\n    if (p === '/options') {\n      res.statusCode = 200\n      res.setHeader('Allow', 'GET, HEAD, OPTIONS')\n      res.end('hello world')\n    }\n\n    if (p === '/html') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/html')\n      res.end('<html></html>')\n    }\n\n    if (p === '/json') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'application/json')\n      res.end(JSON.stringify({\n        name: 'value'\n      }))\n    }\n\n    if (p === '/gzip') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'gzip')\n      zlib.gzip('hello world', (err, buffer) => {\n        if (err) {\n          throw err\n        }\n\n        res.end(buffer)\n      })\n    }\n\n    if (p === '/gzip-truncated') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'gzip')\n      zlib.gzip('hello world', (err, buffer) => {\n        if (err) {\n          throw err\n        }\n\n        // Truncate the CRC checksum and size check at the end of the stream\n        res.end(buffer.slice(0, -8))\n      })\n    }\n\n    if (p === '/gzip-capital') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'GZip')\n      zlib.gzip('hello world', (err, buffer) => {\n        if (err) {\n          throw err\n        }\n\n        res.end(buffer)\n      })\n    }\n\n    if (p === '/deflate') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'deflate')\n      zlib.deflate('hello world', (err, buffer) => {\n        if (err) {\n          throw err\n        }\n\n        res.end(buffer)\n      })\n    }\n\n    if (p === '/brotli') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      if (typeof zlib.createBrotliDecompress === 'function') {\n        res.setHeader('Content-Encoding', 'br')\n        zlib.brotliCompress('hello world', (err, buffer) => {\n          if (err) {\n            throw err\n          }\n\n          res.end(buffer)\n        })\n      }\n    }\n\n    if (p === '/multiunsupported') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      if (typeof zlib.createBrotliDecompress === 'function') {\n        res.setHeader('Content-Encoding', 'br,asd,br')\n        res.end('multiunsupported')\n      }\n    }\n\n    if (p === '/multisupported') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      if (typeof zlib.createBrotliDecompress === 'function') {\n        res.setHeader('Content-Encoding', 'br,br')\n        zlib.brotliCompress('hello world', (err, buffer) => {\n          if (err) {\n            throw err\n          }\n\n          zlib.brotliCompress(buffer, (err, buffer) => {\n            if (err) {\n              throw err\n            }\n\n            res.end(buffer)\n          })\n        })\n      }\n    }\n\n    if (p === '/deflate-raw') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'deflate')\n      zlib.deflateRaw('hello world', (err, buffer) => {\n        if (err) {\n          throw err\n        }\n\n        res.end(buffer)\n      })\n    }\n\n    if (p === '/sdch') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'sdch')\n      res.end('fake sdch string')\n    }\n\n    if (p === '/invalid-content-encoding') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.setHeader('Content-Encoding', 'gzip')\n      res.end('fake gzip string')\n    }\n\n    if (p === '/timeout') {\n      setTimeout(() => {\n        res.statusCode = 200\n        res.setHeader('Content-Type', 'text/plain')\n        res.end('text')\n      }, 1000)\n    }\n\n    if (p === '/slow') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.write('test')\n      setTimeout(() => {\n        res.end('test')\n      }, 1000)\n    }\n\n    if (p === '/cookie') {\n      res.statusCode = 200\n      res.setHeader('Set-Cookie', ['a=1', 'b=1'])\n      res.end('cookie')\n    }\n\n    if (p === '/size/chunk') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      setTimeout(() => {\n        res.write('test')\n      }, 10)\n      setTimeout(() => {\n        res.end('test')\n      }, 20)\n    }\n\n    if (p === '/size/long') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('testtest')\n    }\n\n    if (p === '/redirect/301') {\n      res.statusCode = 301\n      res.setHeader('Location', '/inspect')\n      res.end()\n    }\n\n    if (p === '/redirect/302') {\n      res.statusCode = 302\n      res.setHeader('Location', '/inspect')\n      res.end()\n    }\n\n    if (p === '/redirect/303') {\n      res.statusCode = 303\n      res.setHeader('Location', '/inspect')\n      res.end()\n    }\n\n    if (p === '/redirect/307') {\n      res.statusCode = 307\n      res.setHeader('Location', '/inspect')\n      res.end()\n    }\n\n    if (p === '/redirect/308') {\n      res.statusCode = 308\n      res.setHeader('Location', '/inspect')\n      res.end()\n    }\n\n    if (p === '/redirect/chain') {\n      res.statusCode = 301\n      res.setHeader('Location', '/redirect/301')\n      res.end()\n    }\n\n    if (p.startsWith('/redirect/chain/')) {\n      const count = parseInt(p.split('/').pop()) - 1\n      res.statusCode = 301\n      res.setHeader('Location', count ? `/redirect/chain/${count}` : '/redirect/301')\n      res.end()\n    }\n\n    if (p === '/redirect/no-location') {\n      res.statusCode = 301\n      res.end()\n    }\n\n    if (p === '/redirect/slow') {\n      res.statusCode = 301\n      res.setHeader('Location', '/redirect/301')\n      setTimeout(() => {\n        res.end()\n      }, 1000)\n    }\n\n    if (p === '/redirect/slow-chain') {\n      res.statusCode = 301\n      res.setHeader('Location', '/redirect/slow')\n      setTimeout(() => {\n        res.end()\n      }, 10)\n    }\n\n    if (p === '/redirect/slow-stream') {\n      res.statusCode = 301\n      res.setHeader('Location', '/slow')\n      res.end()\n    }\n\n    if (p === '/redirect/bad-location') {\n      res.socket.write('HTTP/1.1 301\\r\\nLocation: ☃\\r\\nContent-Length: 0\\r\\n')\n      res.socket.end('\\r\\n')\n    }\n\n    if (p === '/error/400') {\n      res.statusCode = 400\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('client error')\n    }\n\n    if (p === '/error/404') {\n      res.statusCode = 404\n      res.setHeader('Content-Encoding', 'gzip')\n      res.end()\n    }\n\n    if (p === '/error/500') {\n      res.statusCode = 500\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('server error')\n    }\n\n    if (p === '/error/reset') {\n      res.destroy()\n    }\n\n    if (p === '/error/premature') {\n      res.writeHead(200, { 'content-length': 50 })\n      res.write('foo')\n      setTimeout(() => {\n        res.destroy()\n      }, 100)\n    }\n\n    if (p === '/error/premature/chunked') {\n      res.writeHead(200, {\n        'Content-Type': 'application/json',\n        'Transfer-Encoding': 'chunked'\n      })\n\n      res.write(`${JSON.stringify({ data: 'hi' })}\\n`)\n\n      setTimeout(() => {\n        res.write(`${JSON.stringify({ data: 'bye' })}\\n`)\n      }, 200)\n\n      setTimeout(() => {\n        res.destroy()\n      }, 400)\n    }\n\n    if (p === '/error/json') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'application/json')\n      res.end('invalid json')\n    }\n\n    if (p === '/no-content') {\n      res.statusCode = 204\n      res.end()\n    }\n\n    if (p === '/no-content/gzip') {\n      res.statusCode = 204\n      res.setHeader('Content-Encoding', 'gzip')\n      res.end()\n    }\n\n    if (p === '/no-content/brotli') {\n      res.statusCode = 204\n      res.setHeader('Content-Encoding', 'br')\n      res.end()\n    }\n\n    if (p === '/not-modified') {\n      res.statusCode = 304\n      res.end()\n    }\n\n    if (p === '/not-modified/gzip') {\n      res.statusCode = 304\n      res.setHeader('Content-Encoding', 'gzip')\n      res.end()\n    }\n\n    if (p === '/inspect') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'application/json')\n      let body = ''\n      request.on('data', c => {\n        body += c\n      })\n      request.on('end', () => {\n        res.end(JSON.stringify({\n          method: request.method,\n          url: request.url,\n          headers: request.headers,\n          body\n        }))\n      })\n    }\n\n    if (p === '/multipart') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'application/json')\n      const busboy = new Busboy({ headers: request.headers })\n      let body = ''\n      busboy.on('file', async (fieldName, file, fileName) => {\n        body += `${fieldName}=${fileName}`\n        // consume file data\n        // eslint-disable-next-line no-empty, no-unused-vars\n        for await (const c of file) {}\n      })\n\n      busboy.on('field', (fieldName, value) => {\n        body += `${fieldName}=${value}`\n      })\n      busboy.on('finish', () => {\n        res.end(JSON.stringify({\n          method: request.method,\n          url: request.url,\n          headers: request.headers,\n          body\n        }))\n      })\n      request.pipe(busboy)\n    }\n\n    if (p === '/m%C3%B6bius') {\n      res.statusCode = 200\n      res.setHeader('Content-Type', 'text/plain')\n      res.end('ok')\n    }\n  }\n}\n"
  },
  {
    "path": "test/node-platform-objects.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { markAsUncloneable } = require('node:worker_threads')\nconst { Response, Request, FormData, Headers, ErrorEvent, MessageEvent, CloseEvent, EventSource, WebSocket } = require('..')\nconst { CacheStorage } = require('../lib/web/cache/cachestorage')\nconst { Cache } = require('../lib/web/cache/cache')\nconst { kConstruct } = require('../lib/core/symbols')\n\ntest('unserializable web instances should be uncloneable if node exposes the api', (t) => {\n  if (markAsUncloneable !== undefined) {\n    t = tspl(t, { plan: 11 })\n    const uncloneables = [\n      { Uncloneable: Response, brand: 'Response' },\n      { Uncloneable: Request, value: 'http://localhost', brand: 'Request' },\n      { Uncloneable: FormData, brand: 'FormData' },\n      { Uncloneable: MessageEvent, value: 'dummy event', brand: 'MessageEvent' },\n      { Uncloneable: CloseEvent, value: 'dummy event', brand: 'CloseEvent' },\n      { Uncloneable: ErrorEvent, value: 'dummy event', brand: 'ErrorEvent' },\n      { Uncloneable: EventSource, value: 'http://localhost', brand: 'EventSource', doneCb: (entity) => entity.close() },\n      { Uncloneable: Headers, brand: 'Headers' },\n      { Uncloneable: WebSocket, value: 'http://localhost', brand: 'WebSocket' },\n      { Uncloneable: Cache, value: kConstruct, brand: 'Cache' },\n      { Uncloneable: CacheStorage, value: kConstruct, brand: 'CacheStorage' }\n    ]\n    uncloneables.forEach((platformEntity) => {\n      const entity = new platformEntity.Uncloneable(platformEntity.value)\n      t.throws(() => structuredClone(entity),\n        DOMException,\n        `Cloning ${platformEntity.brand} should throw DOMException`)\n\n      platformEntity.doneCb?.(entity)\n    })\n  }\n})\n"
  },
  {
    "path": "test/node-test/abort-controller.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { AbortController: NPMAbortController } = require('abort-controller')\nconst { Client, errors } = require('../..')\nconst { createServer } = require('node:http')\nconst { createReadStream } = require('node:fs')\nconst { wrapWithAsyncIterable } = require('../utils/async-iterators')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst controllers = [{\n  AbortControllerImpl: NPMAbortController,\n  controllerName: 'npm-abortcontroller-shim'\n}]\n\nif (global.AbortController) {\n  controllers.push({\n    AbortControllerImpl: global.AbortController,\n    controllerName: 'native-abortcontroller'\n  })\n}\nfor (const { AbortControllerImpl, controllerName } of controllers) {\n  test(`Abort ${controllerName} before creating request`, async (t) => {\n    const p = tspl(t, { plan: 1 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      p.fail()\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      const abortController = new AbortControllerImpl()\n      t.after(client.destroy.bind(client))\n\n      abortController.abort()\n\n      client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n      })\n    })\n\n    await p.completed\n  })\n\n  test(`Abort ${controllerName} before sending request (no body)`, async (t) => {\n    const p = tspl(t, { plan: 3 })\n\n    let count = 0\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      if (count === 1) {\n        p.fail('The second request should never be executed')\n      }\n      count += 1\n      res.end('hello')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      const abortController = new AbortControllerImpl()\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'GET' }, (err, response) => {\n        p.ifError(err)\n        const bufs = []\n        response.body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        response.body.on('end', () => {\n          p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n\n      client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n      })\n\n      abortController.abort()\n    })\n\n    await p.completed\n  })\n\n  test(`Abort ${controllerName} while waiting response (no body)`, async (t) => {\n    const p = tspl(t, { plan: 1 })\n\n    const abortController = new AbortControllerImpl()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      abortController.abort()\n      res.setHeader('content-type', 'text/plain')\n      res.end('hello world')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n      })\n    })\n\n    await p.completed\n  })\n\n  test(`Abort ${controllerName} while waiting response (write headers started) (no body)`, async (t) => {\n    const p = tspl(t, { plan: 1 })\n\n    const abortController = new AbortControllerImpl()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.flushHeaders()\n      abortController.abort()\n      res.end('hello world')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n      })\n    })\n\n    await p.completed\n  })\n\n  test(`Abort ${controllerName} while waiting response (write headers and write body started) (no body)`, async (t) => {\n    const p = tspl(t, { plan: 2 })\n\n    const abortController = new AbortControllerImpl()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.write('hello')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n        p.ifError(err)\n        response.body.on('data', () => {\n          abortController.abort()\n        })\n        response.body.on('error', err => {\n          p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n        })\n      })\n    })\n\n    await p.completed\n  })\n\n  function waitingWithBody (body, type) {\n    test(`Abort ${controllerName} while waiting response (with body ${type})`, async (t) => {\n      const p = tspl(t, { plan: 1 })\n\n      const abortController = new AbortControllerImpl()\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        abortController.abort()\n        res.setHeader('content-type', 'text/plain')\n        res.end('hello world')\n      })\n      t.after(closeServerAsPromise(server))\n\n      server.listen(0, () => {\n        const client = new Client(`http://localhost:${server.address().port}`)\n        t.after(client.destroy.bind(client))\n\n        client.request({ path: '/', method: 'POST', body, signal: abortController.signal }, (err, response) => {\n          p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n        })\n      })\n      await p.completed\n    })\n  }\n\n  waitingWithBody('hello', 'string')\n  waitingWithBody(createReadStream(__filename), 'stream')\n  waitingWithBody(new Uint8Array([42]), 'Uint8Array')\n  waitingWithBody(wrapWithAsyncIterable(createReadStream(__filename)), 'async-iterator')\n\n  function writeHeadersStartedWithBody (body, type) {\n    test(`Abort ${controllerName} while waiting response (write headers started) (with body ${type})`, async (t) => {\n      const p = tspl(t, { plan: 1 })\n\n      const abortController = new AbortControllerImpl()\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        res.writeHead(200, { 'content-type': 'text/plain' })\n        res.flushHeaders()\n        abortController.abort()\n        res.end('hello world')\n      })\n      t.after(closeServerAsPromise(server))\n\n      server.listen(0, () => {\n        const client = new Client(`http://localhost:${server.address().port}`)\n        t.after(client.destroy.bind(client))\n\n        client.request({ path: '/', method: 'POST', body, signal: abortController.signal }, (err, response) => {\n          p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n        })\n      })\n      await p.completed\n    })\n  }\n\n  writeHeadersStartedWithBody('hello', 'string')\n  writeHeadersStartedWithBody(createReadStream(__filename), 'stream')\n  writeHeadersStartedWithBody(new Uint8Array([42]), 'Uint8Array')\n  writeHeadersStartedWithBody(wrapWithAsyncIterable(createReadStream(__filename)), 'async-iterator')\n\n  function writeBodyStartedWithBody (body, type) {\n    test(`Abort ${controllerName} while waiting response (write headers and write body started) (with body ${type})`, async (t) => {\n      const p = tspl(t, { plan: 2 })\n\n      const abortController = new AbortControllerImpl()\n      const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n        res.writeHead(200, { 'content-type': 'text/plain' })\n        res.write('hello')\n      })\n      t.after(closeServerAsPromise(server))\n\n      server.listen(0, () => {\n        const client = new Client(`http://localhost:${server.address().port}`)\n        t.after(client.destroy.bind(client))\n\n        client.request({ path: '/', method: 'POST', body, signal: abortController.signal }, (err, response) => {\n          p.ifError(err)\n          response.body.on('data', () => {\n            abortController.abort()\n          })\n          response.body.on('error', err => {\n            p.ok(err instanceof errors.RequestAbortedError || err instanceof DOMException)\n          })\n        })\n      })\n      await p.completed\n    })\n  }\n\n  writeBodyStartedWithBody('hello', 'string')\n  writeBodyStartedWithBody(createReadStream(__filename), 'stream')\n  writeBodyStartedWithBody(new Uint8Array([42]), 'Uint8Array')\n  writeBodyStartedWithBody(wrapWithAsyncIterable(createReadStream(__filename), 'async-iterator'))\n}\n"
  },
  {
    "path": "test/node-test/abort-event-emitter.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst EventEmitter = require('node:events')\nconst { Client, errors } = require('../..')\nconst { createServer } = require('node:http')\nconst { createReadStream } = require('node:fs')\nconst { Readable } = require('node:stream')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { wrapWithAsyncIterable } = require('../utils/async-iterators')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('Abort before sending request (no body)', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  let count = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (count === 1) {\n      p.fail('The second request should never be executed')\n    }\n    count += 1\n    res.end('hello')\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const ee = new EventEmitter()\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      p.ifError(err)\n      const bufs = []\n      response.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      response.body.on('end', () => {\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    const body = new Readable({ read () { } })\n    body.on('error', (err) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n    client.request({\n      path: '/',\n      method: 'GET',\n      signal: ee,\n      body\n    }, (err, response) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n\n    ee.emit('abort')\n  })\n\n  await p.completed\n})\n\ntest('Abort before sending request (no body) async iterator', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  let count = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (count === 1) {\n      t.fail('The second request should never be executed')\n    }\n    count += 1\n    res.end('hello')\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const ee = new EventEmitter()\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      p.ifError(err)\n      const bufs = []\n      response.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      response.body.on('end', () => {\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    const body = wrapWithAsyncIterable(new Readable({ read () { } }))\n    client.request({\n      path: '/',\n      method: 'GET',\n      signal: ee,\n      body\n    }, (err, response) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n\n    ee.emit('abort')\n  })\n\n  await p.completed\n})\n\ntest('Abort while waiting response (no body)', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const ee = new EventEmitter()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    ee.emit('abort')\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello world')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n  })\n\n  await p.completed\n})\n\ntest('Abort while waiting response (write headers started) (no body)', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const ee = new EventEmitter()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.flushHeaders()\n    ee.emit('abort')\n    res.end('hello world')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n  })\n\n  await p.completed\n})\n\ntest('Abort while waiting response (write headers and write body started) (no body)', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const ee = new EventEmitter()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.write('hello')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => {\n      p.ifError(err)\n      response.body.on('data', () => {\n        ee.emit('abort')\n      })\n      response.body.on('error', err => {\n        p.ok(err instanceof errors.RequestAbortedError)\n      })\n    })\n  })\n  await p.completed\n})\n\nfunction waitingWithBody (body, type) {\n  test(`Abort while waiting response (with body ${type})`, async (t) => {\n    const p = tspl(t, { plan: 1 })\n\n    const ee = new EventEmitter()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      ee.emit('abort')\n      res.setHeader('content-type', 'text/plain')\n      res.end('hello world')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'POST', body, signal: ee }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError)\n      })\n    })\n    await p.completed\n  })\n}\n\nwaitingWithBody('hello', 'string')\nwaitingWithBody(createReadStream(__filename), 'stream')\nwaitingWithBody(new Uint8Array([42]), 'Uint8Array')\nwaitingWithBody(wrapWithAsyncIterable(createReadStream(__filename)), 'async-iterator')\n\nfunction writeHeadersStartedWithBody (body, type) {\n  test(`Abort while waiting response (write headers started) (with body ${type})`, async (t) => {\n    const p = tspl(t, { plan: 1 })\n\n    const ee = new EventEmitter()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.flushHeaders()\n      ee.emit('abort')\n      res.end('hello world')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'POST', body, signal: ee }, (err, response) => {\n        p.ok(err instanceof errors.RequestAbortedError)\n      })\n    })\n    await p.completed\n  })\n}\n\nwriteHeadersStartedWithBody('hello', 'string')\nwriteHeadersStartedWithBody(createReadStream(__filename), 'stream')\nwriteHeadersStartedWithBody(new Uint8Array([42]), 'Uint8Array')\nwriteHeadersStartedWithBody(wrapWithAsyncIterable(createReadStream(__filename)), 'async-iterator')\n\nfunction writeBodyStartedWithBody (body, type) {\n  test(`Abort while waiting response (write headers and write body started) (with body ${type})`, async (t) => {\n    const p = tspl(t, { plan: 2 })\n\n    const ee = new EventEmitter()\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.write('hello')\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({ path: '/', method: 'POST', body, signal: ee }, (err, response) => {\n        p.ifError(err)\n        response.body.on('data', () => {\n          ee.emit('abort')\n        })\n        response.body.on('error', err => {\n          p.ok(err instanceof errors.RequestAbortedError)\n        })\n      })\n    })\n    await p.completed\n  })\n}\n\nwriteBodyStartedWithBody('hello', 'string')\nwriteBodyStartedWithBody(createReadStream(__filename), 'stream')\nwriteBodyStartedWithBody(new Uint8Array([42]), 'Uint8Array')\nwriteBodyStartedWithBody(wrapWithAsyncIterable(createReadStream(__filename)), 'async-iterator')\n"
  },
  {
    "path": "test/node-test/agent.js",
    "content": "'use strict'\n\nconst { describe, test, after } = require('node:test')\nconst assert = require('node:assert/strict')\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst { PassThrough } = require('node:stream')\nconst { kRunning } = require('../../lib/core/symbols')\nconst {\n  Agent,\n  errors,\n  request,\n  stream,\n  pipeline,\n  Pool,\n  setGlobalDispatcher,\n  getGlobalDispatcher\n} = require('../..')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ndescribe('setGlobalDispatcher', () => {\n  after(() => {\n    // reset globalAgent to a fresh Agent instance for later tests\n    setGlobalDispatcher(new Agent())\n  })\n  test('fails if agent does not implement `get` method', t => {\n    const p = tspl(t, { plan: 1 })\n    p.throws(() => setGlobalDispatcher({ dispatch: 'not a function' }), errors.InvalidArgumentError)\n  })\n  test('sets global agent', async t => {\n    const p = tspl(t, { plan: 2 })\n    p.doesNotThrow(() => setGlobalDispatcher(new Agent()))\n    p.doesNotThrow(() => setGlobalDispatcher({ dispatch: () => {} }))\n  })\n})\n\ntest('Agent', t => {\n  const p = tspl(t, { plan: 1 })\n\n  p.doesNotThrow(() => new Agent())\n})\n\ntest('Agent enforces maxOrigins', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const dispatcher = new Agent({\n    maxOrigins: 1,\n    keepAliveMaxTimeout: 100,\n    keepAliveTimeout: 100\n  })\n  t.after(() => dispatcher.close())\n\n  const handler = (_req, res) => {\n    setTimeout(() => res.end('ok'), 50)\n  }\n\n  const server1 = http.createServer({ joinDuplicateHeaders: true }, handler)\n  server1.listen(0)\n  await once(server1, 'listening')\n  t.after(closeServerAsPromise(server1))\n\n  const server2 = http.createServer({ joinDuplicateHeaders: true }, handler)\n  server2.listen(0)\n  await once(server2, 'listening')\n  t.after(closeServerAsPromise(server2))\n\n  try {\n    await Promise.all([\n      request(`http://localhost:${server1.address().port}`, { dispatcher }),\n      request(`http://localhost:${server2.address().port}`, { dispatcher })\n    ])\n  } catch (err) {\n    p.ok(err instanceof errors.MaxOriginsReachedError)\n  }\n\n  await p.completed\n})\n\ntest('agent should call callback after closing internal pools', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const dispatcher = new Agent()\n\n    const origin = `http://localhost:${server.address().port}`\n\n    request(origin, { dispatcher })\n      .then(() => {\n        // first request should resolve\n        p.ok(1)\n      })\n      .catch(err => {\n        p.fail(err)\n      })\n\n    dispatcher.once('connect', () => {\n      dispatcher.close(() => {\n        request(origin, { dispatcher })\n          .then(() => {\n            p.fail('second request should not resolve')\n          })\n          .catch(err => {\n            p.ok(err instanceof errors.ClientDestroyedError)\n          })\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('agent close throws when callback is not a function', t => {\n  const p = tspl(t, { plan: 1 })\n  const dispatcher = new Agent()\n  try {\n    dispatcher.close({})\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n  }\n})\n\ntest('agent should close internal pools', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const dispatcher = new Agent()\n\n    const origin = `http://localhost:${server.address().port}`\n\n    request(origin, { dispatcher })\n      .then(() => {\n        // first request should resolve\n        p.ok(1)\n      })\n      .catch(err => {\n        p.fail(err)\n      })\n\n    dispatcher.once('connect', () => {\n      dispatcher.close()\n        .then(() => request(origin, { dispatcher }))\n        .then(() => {\n          p.fail('second request should not resolve')\n        })\n        .catch(err => {\n          p.ok(err instanceof errors.ClientDestroyedError)\n        })\n    })\n  })\n\n  await p.completed\n})\n\ntest('agent should destroy internal pools and call callback', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const dispatcher = new Agent()\n\n    const origin = `http://localhost:${server.address().port}`\n\n    request(origin, { dispatcher })\n      .then(() => {\n        p.fail()\n      })\n      .catch(err => {\n        p.ok(err instanceof errors.ClientDestroyedError)\n      })\n\n    dispatcher.once('connect', () => {\n      dispatcher.destroy(() => {\n        request(origin, { dispatcher })\n          .then(() => {\n            p.fail()\n          })\n          .catch(err => {\n            p.ok(err instanceof errors.ClientDestroyedError)\n          })\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('agent destroy throws when callback is not a function', t => {\n  const p = tspl(t, { plan: 1 })\n  const dispatcher = new Agent()\n  try {\n    dispatcher.destroy(new Error('mock error'), {})\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n  }\n})\n\ntest('agent close/destroy callback with error', t => {\n  const p = tspl(t, { plan: 4 })\n  const dispatcher = new Agent()\n  p.strictEqual(dispatcher.closed, false)\n  dispatcher.close()\n  p.strictEqual(dispatcher.closed, true)\n  p.strictEqual(dispatcher.destroyed, false)\n  dispatcher.destroy(new Error('mock error'))\n  p.strictEqual(dispatcher.destroyed, true)\n})\n\ntest('agent should destroy internal pools', async t => {\n  const p = tspl(t, { plan: 2 })\n\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const dispatcher = new Agent()\n\n    const origin = `http://localhost:${server.address().port}`\n\n    request(origin, { dispatcher })\n      .then(() => {\n        p.fail()\n      })\n      .catch(err => {\n        p.ok(err instanceof errors.ClientDestroyedError)\n      })\n\n    dispatcher.once('connect', () => {\n      dispatcher.destroy()\n        .then(() => request(origin, { dispatcher }))\n        .then(() => {\n          p.fail()\n        })\n        .catch(err => {\n          p.ok(err instanceof errors.ClientDestroyedError)\n        })\n    })\n  })\n\n  await p.completed\n})\n\ntest('multiple connections', async t => {\n  const connections = 3\n  const p = tspl(t, { plan: 6 * connections })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      Connection: 'keep-alive',\n      'Keep-Alive': 'timeout=1s'\n    })\n    res.end('ok')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const origin = `http://localhost:${server.address().port}`\n    const dispatcher = new Agent({ connections })\n\n    t.after(() => { dispatcher.close.bind(dispatcher)() })\n\n    dispatcher.on('connect', (origin, [dispatcher]) => {\n      p.ok(dispatcher)\n    })\n    dispatcher.on('disconnect', (origin, [dispatcher], error) => {\n      p.ok(dispatcher)\n      p.ok(error instanceof errors.InformationalError)\n      p.strictEqual(error.code, 'UND_ERR_INFO')\n      p.strictEqual(error.message, 'reset')\n    })\n\n    for (let i = 0; i < connections; i++) {\n      try {\n        await request(origin, { dispatcher })\n        p.ok(1)\n      } catch (err) {\n        p.fail(err)\n      }\n    }\n  })\n\n  await p.completed\n})\n\ntest('agent factory supports URL parameter', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const noopHandler = {\n    onConnect () {},\n    onHeaders () {},\n    onData () {},\n    onComplete () {\n      server.close()\n    },\n    onError (err) {\n      throw err\n    }\n  }\n\n  const dispatcher = new Agent({\n    factory: (origin, opts) => {\n      p.ok(origin instanceof URL)\n      return new Pool(origin, opts)\n    }\n  })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('asd')\n  })\n\n  server.listen(0, () => {\n    p.doesNotThrow(() => dispatcher.dispatch({\n      origin: new URL(`http://localhost:${server.address().port}`),\n      path: '/',\n      method: 'GET'\n    }, noopHandler))\n  })\n\n  await p.completed\n})\n\ntest('agent factory supports string parameter', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const noopHandler = {\n    onConnect () {},\n    onHeaders () {},\n    onData () {},\n    onComplete () {\n      server.close()\n    },\n    onError (err) {\n      throw err\n    }\n  }\n\n  const dispatcher = new Agent({\n    factory: (origin, opts) => {\n      p.ok(typeof origin === 'string')\n      return new Pool(origin, opts)\n    }\n  })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('asd')\n  })\n\n  server.listen(0, () => {\n    p.doesNotThrow(() => dispatcher.dispatch({\n      origin: `http://localhost:${server.address().port}`,\n      path: '/',\n      method: 'GET'\n    }, noopHandler))\n  })\n\n  await p.completed\n})\n\ntest('with globalAgent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    request(`http://localhost:${server.address().port}`)\n      .then(({ statusCode, headers, body }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n      .catch(err => {\n        p.fail(err)\n      })\n  })\n\n  await p.completed\n})\n\ntest('with local agent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  const dispatcher = new Agent({\n    connect: {\n      servername: 'agent1'\n    }\n  })\n\n  server.listen(0, () => {\n    request(`http://localhost:${server.address().port}`, { dispatcher })\n      .then(({ statusCode, headers, body }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n      .catch(err => {\n        p.fail(err)\n      })\n  })\n\n  await p.completed\n})\n\ntest('fails with invalid args', t => {\n  assert.throws(() => request(), errors.InvalidArgumentError, 'throws on missing url argument')\n  assert.throws(() => request(''), errors.InvalidArgumentError, 'throws on invalid url')\n  assert.throws(() => request({}), errors.InvalidArgumentError, 'throws on missing url.origin argument')\n  assert.throws(() => request({ origin: '' }), errors.InvalidArgumentError, 'throws on invalid url.origin argument')\n  assert.throws(() => request('https://example.com', { path: 0 }), errors.InvalidArgumentError, 'throws on opts.path argument')\n  assert.throws(() => request('https://example.com', { agent: new Agent() }), errors.InvalidArgumentError, 'throws on opts.path argument')\n  assert.throws(() => request('https://example.com', 'asd'), errors.InvalidArgumentError, 'throws on non object opts argument')\n})\n\ntest('with globalAgent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    stream(\n      `http://localhost:${server.address().port}`,\n      {\n        opaque: new PassThrough()\n      },\n      ({ statusCode, headers, opaque: pt }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        pt.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        pt.on('end', () => {\n          p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n        })\n        pt.on('error', () => {\n          p.fail()\n        })\n        return pt\n      }\n    )\n  })\n\n  await p.completed\n})\n\ntest('with a local agent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  const dispatcher = new Agent()\n\n  dispatcher.on('connect', (origin, [dispatcher]) => {\n    p.ok(dispatcher)\n    p.strictEqual(dispatcher[kRunning], 0)\n    process.nextTick(() => {\n      p.strictEqual(dispatcher[kRunning], 1)\n    })\n  })\n\n  server.listen(0, () => {\n    stream(\n      `http://localhost:${server.address().port}`,\n      {\n        dispatcher,\n        opaque: new PassThrough()\n      },\n      ({ statusCode, headers, opaque: pt }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        pt.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        pt.on('end', () => {\n          p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n        })\n        pt.on('error', (err) => {\n          p.fail(err)\n        })\n        return pt\n      }\n    )\n  })\n\n  await p.completed\n})\n\ntest('stream: fails with invalid URL', t => {\n  const p = tspl(t, { plan: 4 })\n  p.throws(() => stream(), errors.InvalidArgumentError, 'throws on missing url argument')\n  p.throws(() => stream(''), errors.InvalidArgumentError, 'throws on invalid url')\n  p.throws(() => stream({}), errors.InvalidArgumentError, 'throws on missing url.origin argument')\n  p.throws(() => stream({ origin: '' }), errors.InvalidArgumentError, 'throws on invalid url.origin argument')\n})\n\ntest('with globalAgent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const bufs = []\n\n    pipeline(\n      `http://localhost:${server.address().port}`,\n      {},\n      ({ statusCode, headers, body }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        return body\n      }\n    )\n      .end()\n      .on('data', buf => {\n        bufs.push(buf)\n      })\n      .on('end', () => {\n        p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n      })\n      .on('error', (err) => {\n        p.fail(err)\n      })\n  })\n\n  await p.completed\n})\n\ntest('with a local agent', async t => {\n  const p = tspl(t, { plan: 6 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  const dispatcher = new Agent()\n\n  server.listen(0, () => {\n    const bufs = []\n\n    pipeline(\n      `http://localhost:${server.address().port}`,\n      { dispatcher },\n      ({ statusCode, headers, body }) => {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        return body\n      }\n    )\n      .end()\n      .on('data', buf => {\n        bufs.push(buf)\n      })\n      .on('end', () => {\n        p.strictEqual(wanted, Buffer.concat(bufs).toString('utf8'))\n      })\n      .on('error', () => {\n        p.fail()\n      })\n  })\n\n  await p.completed\n})\n\ntest('pipeline: fails with invalid URL', t => {\n  const p = tspl(t, { plan: 4 })\n  p.throws(() => pipeline(), errors.InvalidArgumentError, 'throws on missing url argument')\n  p.throws(() => pipeline(''), errors.InvalidArgumentError, 'throws on invalid url')\n  p.throws(() => pipeline({}), errors.InvalidArgumentError, 'throws on missing url.origin argument')\n  p.throws(() => pipeline({ origin: '' }), errors.InvalidArgumentError, 'throws on invalid url.origin argument')\n})\n\ntest('pipeline: fails with invalid onInfo', async (t) => {\n  const p = tspl(t, { plan: 2 })\n  pipeline({ origin: 'http://localhost', path: '/', onInfo: 'foo' }, () => {}).on('error', (err) => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.equal(err.message, 'invalid onInfo callback')\n  })\n  await p.completed\n})\n\ntest('request: fails with invalid onInfo', async (t) => {\n  try {\n    await request({ origin: 'http://localhost', path: '/', onInfo: 'foo' })\n    assert.fail('should throw')\n  } catch (e) {\n    assert.ok(e)\n    assert.strictEqual(e.message, 'invalid onInfo callback')\n  }\n})\n\ntest('stream: fails with invalid onInfo', async (t) => {\n  try {\n    await stream({ origin: 'http://localhost', path: '/', onInfo: 'foo' }, () => new PassThrough())\n    assert.fail('should throw')\n  } catch (e) {\n    assert.ok(e)\n    assert.strictEqual(e.message, 'invalid onInfo callback')\n  }\n})\n\ntest('constructor validations', t => {\n  const p = tspl(t, { plan: 3 })\n  p.throws(() => new Agent({ factory: 'ASD' }), errors.InvalidArgumentError, 'throws on invalid opts argument')\n  p.throws(() => new Agent({ maxOrigins: -1 }), errors.InvalidArgumentError, 'maxOrigins must be a number greater than 0')\n  p.throws(() => new Agent({ maxOrigins: 'foo' }), errors.InvalidArgumentError, 'maxOrigins must be a number greater than 0')\n})\n\ntest('dispatch validations', async t => {\n  const dispatcher = new Agent()\n\n  const noopHandler = {\n    onConnect () {},\n    onHeaders () {},\n    onData () {},\n    onComplete () {\n      server.close()\n    },\n    onError (err) {\n      throw err\n    }\n  }\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('asd')\n  })\n\n  const p = tspl(t, { plan: 6 })\n  p.throws(() => dispatcher.dispatch('ASD'), errors.InvalidArgumentError, 'throws on missing handler')\n  p.throws(() => dispatcher.dispatch('ASD', noopHandler), errors.InvalidArgumentError, 'throws on invalid opts argument type')\n  p.throws(() => dispatcher.dispatch({}, noopHandler), errors.InvalidArgumentError, 'throws on invalid opts.origin argument')\n  p.throws(() => dispatcher.dispatch({ origin: '' }, noopHandler), errors.InvalidArgumentError, 'throws on invalid opts.origin argument')\n  p.throws(() => dispatcher.dispatch({}, {}), errors.InvalidArgumentError, 'throws on invalid handler.onError')\n\n  server.listen(0, () => {\n    p.doesNotThrow(() => dispatcher.dispatch({\n      origin: new URL(`http://localhost:${server.address().port}`),\n      path: '/',\n      method: 'GET'\n    }, noopHandler))\n  })\n\n  await p.completed\n})\n\ntest('drain', async t => {\n  const p = tspl(t, { plan: 2 })\n\n  const dispatcher = new Agent({\n    connections: 1,\n    pipelining: 1\n  })\n\n  dispatcher.on('drain', () => {\n    p.ok(1)\n  })\n\n  class Handler {\n    onConnect () {}\n    onHeaders () {}\n    onData () {}\n    onComplete () {}\n    onError () {\n      p.fail()\n    }\n  }\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('asd')\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    p.strictEqual(dispatcher.dispatch({\n      origin: `http://localhost:${server.address().port}`,\n      method: 'GET',\n      path: '/'\n    }, new Handler()), false)\n  })\n\n  await p.completed\n})\n\ntest('global api', async t => {\n  const p = tspl(t, { plan: 6 * 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/bar') {\n      p.strictEqual(req.method, 'PUT')\n      p.strictEqual(req.url, '/bar')\n    } else {\n      p.strictEqual(req.method, 'GET')\n      p.strictEqual(req.url, '/foo')\n    }\n    req.pipe(res)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const origin = `http://localhost:${server.address().port}`\n    await request(origin, { path: '/foo' })\n    await request(`${origin}/foo`)\n    await request({ origin, path: '/foo' })\n    await stream({ origin, path: '/foo' }, () => new PassThrough())\n    await request({ protocol: 'http:', hostname: 'localhost', port: server.address().port, path: '/foo' })\n    await request(`${origin}/bar`, { body: 'asd' })\n  })\n\n  await p.completed\n})\n\ntest('global api throws', t => {\n  const origin = 'http://asd'\n  assert.throws(() => request(`${origin}/foo`, { path: '/foo' }), errors.InvalidArgumentError)\n  assert.throws(() => request({ origin, path: 0 }, { path: '/foo' }), errors.InvalidArgumentError)\n  assert.throws(() => request({ origin, pathname: 0 }, { path: '/foo' }), errors.InvalidArgumentError)\n  assert.throws(() => request({ origin: 0 }, { path: '/foo' }), errors.InvalidArgumentError)\n  assert.throws(() => request(0), errors.InvalidArgumentError)\n  assert.throws(() => request(1), errors.InvalidArgumentError)\n})\n\ntest('unreachable request rejects and can be caught', async t => {\n  const p = tspl(t, { plan: 1 })\n\n  request('https://thisis.not/avalid/url').catch(() => {\n    p.ok(1)\n  })\n\n  await p.completed\n})\n\ntest('connect is not valid', t => {\n  const p = tspl(t, { plan: 1 })\n\n  p.throws(() => new Agent({ connect: false }), errors.InvalidArgumentError, 'connect must be a function or an object')\n})\n\ntest('the dispatcher is truly global', t => {\n  const agent = getGlobalDispatcher()\n  assert.ok(require.resolve('../../index.js') in require.cache)\n  delete require.cache[require.resolve('../../index.js')]\n  assert.strictEqual(require.resolve('../../index.js') in require.cache, false)\n  const undiciFresh = require('../../index.js')\n  assert.ok(require.resolve('../../index.js') in require.cache)\n  assert.strictEqual(agent, undiciFresh.getGlobalDispatcher())\n})\n\ntest('stats', async t => {\n  const p = tspl(t, { plan: 7 })\n  const wanted = 'payload'\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.end(wanted)\n  })\n\n  t.after(closeServerAsPromise(server))\n\n  const dispatcher = new Agent({\n    connect: {\n      servername: 'agent1'\n    }\n  })\n\n  server.listen(0, () => {\n    request(`http://localhost:${server.address().port}`, { dispatcher })\n      .then(({ statusCode, headers, body }) => {\n        p.strictEqual(statusCode, 200)\n        const originForStats = `http://localhost:${server.address().port}`\n        const agentStats = dispatcher.stats[originForStats]\n        p.strictEqual(agentStats.connected, 1)\n        p.strictEqual(agentStats.pending, 0)\n        p.strictEqual(agentStats.running, 0)\n        p.strictEqual(agentStats.size, 0)\n      })\n      .catch(err => {\n        p.fail(err)\n      })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/async_hooks.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Client } = require('../..')\nconst { createServer } = require('node:http')\nconst { createHook, executionAsyncId } = require('node:async_hooks')\nconst { readFile } = require('node:fs')\nconst { PassThrough } = require('node:stream')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nconst transactions = new Map()\n\nfunction getCurrentTransaction () {\n  const asyncId = executionAsyncId()\n  return transactions.has(asyncId) ? transactions.get(asyncId) : null\n}\n\nfunction setCurrentTransaction (trans) {\n  const asyncId = executionAsyncId()\n  transactions.set(asyncId, trans)\n}\n\nconst hook = createHook({\n  init (asyncId, type, triggerAsyncId, resource) {\n    if (type === 'TIMERWRAP') return\n    // process._rawDebug(type + ' ' + asyncId)\n    transactions.set(asyncId, getCurrentTransaction())\n  },\n  destroy (asyncId) {\n    transactions.delete(asyncId)\n  }\n})\n\nhook.enable()\n\ntest('async hooks', async (t) => {\n  const p = tspl(t, { plan: 31 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    readFile(__filename, (err, buf) => {\n      p.ifError(err)\n      const buf1 = buf.slice(0, buf.length / 2)\n      const buf2 = buf.slice(buf.length / 2)\n      // we split the file so that it's received in 2 chunks\n      // and it should restore the state on the second\n      res.write(buf1)\n      setTimeout(() => {\n        res.end(buf2)\n      }, 10)\n    })\n  })\n  t.after(() => server.close.bind(server)())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => client.destroy.bind(client)())\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      body.resume()\n      p.deepStrictEqual(getCurrentTransaction(), null)\n\n      setCurrentTransaction({ hello: 'world2' })\n\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world2' })\n\n        body.once('data', () => {\n          p.ok(1, 1)\n          body.resume()\n        })\n\n        body.on('end', () => {\n          p.ok(1, 1)\n        })\n      })\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      body.resume()\n      p.deepStrictEqual(getCurrentTransaction(), null)\n\n      setCurrentTransaction({ hello: 'world' })\n\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world' })\n\n        body.once('data', () => {\n          p.ok(1)\n          body.resume()\n        })\n\n        body.on('end', () => {\n          p.ok(1)\n        })\n      })\n    })\n\n    client.request({ path: '/', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      body.resume()\n      p.deepStrictEqual(getCurrentTransaction(), null)\n\n      setCurrentTransaction({ hello: 'world' })\n\n      client.request({ path: '/', method: 'HEAD' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world' })\n\n        body.once('data', () => {\n          p.ok(1)\n          body.resume()\n        })\n\n        body.on('end', () => {\n          p.ok(1)\n        })\n      })\n    })\n\n    client.stream({ path: '/', method: 'GET' }, () => {\n      p.strictEqual(getCurrentTransaction(), null)\n      return new PassThrough().resume()\n    }, (err) => {\n      p.ifError(err)\n      p.deepStrictEqual(getCurrentTransaction(), null)\n\n      setCurrentTransaction({ hello: 'world' })\n\n      client.stream({ path: '/', method: 'GET' }, () => {\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world' })\n        return new PassThrough().resume()\n      }, (err) => {\n        p.ifError(err)\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world' })\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('async hooks client is destroyed', async (t) => {\n  const p = tspl(t, { plan: 7 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    readFile(__filename, (err, buf) => {\n      p.ifError(err)\n      res.write('asd')\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, { body }) => {\n      p.ifError(err)\n      body.resume()\n      body.on('error', (err) => {\n        p.ok(err)\n      })\n      p.deepStrictEqual(getCurrentTransaction(), null)\n\n      setCurrentTransaction({ hello: 'world2' })\n\n      client.request({ path: '/', method: 'GET' }, (err) => {\n        p.strictEqual(err.message, 'The client is destroyed')\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world2' })\n      })\n      client.destroy((err) => {\n        p.ifError(err)\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('async hooks pipeline handler', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('hello')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    setCurrentTransaction({ hello: 'world2' })\n\n    client\n      .pipeline({ path: '/', method: 'GET' }, ({ body }) => {\n        p.deepStrictEqual(getCurrentTransaction(), { hello: 'world2' })\n        return body\n      })\n      .on('close', () => {\n        p.ok(1)\n      })\n      .resume()\n      .end()\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/autoselectfamily.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst dgram = require('node:dgram')\nconst { Resolver } = require('node:dns')\nconst dnsPacket = require('dns-packet')\nconst { createServer } = require('node:http')\nconst { Client, Agent, request } = require('../..')\nconst { tspl } = require('@matteo.collina/tspl')\n\n/*\n * IMPORTANT\n *\n * As only some version of Node have autoSelectFamily enabled by default (>= 20), make sure the option is always\n * explicitly passed in tests in this file to avoid compatibility problems across release lines.\n *\n */\nconst skip = false\n\nfunction _lookup (resolver, hostname, options, cb) {\n  resolver.resolve(hostname, 'ANY', (err, replies) => {\n    if (err) {\n      return cb(err)\n    }\n\n    const hosts = replies\n      .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))\n      .sort((a, b) => b.family - a.family)\n\n    if (options.all === true) {\n      return cb(null, hosts)\n    }\n\n    return cb(null, hosts[0].address, hosts[0].family)\n  })\n}\n\nfunction createDnsServer (ipv6Addr, ipv4Addr, cb) {\n  // Create a DNS server which replies with an AAAA and an A record for the same host\n  const socket = dgram.createSocket('udp4')\n\n  socket.on('message', (msg, { address, port }) => {\n    const parsed = dnsPacket.decode(msg)\n\n    const response = dnsPacket.encode({\n      type: 'answer',\n      id: parsed.id,\n      questions: parsed.questions,\n      answers: [\n        { type: 'AAAA', class: 'IN', name: 'example.org', data: '::1', ttl: 123 },\n        { type: 'A', class: 'IN', name: 'example.org', data: '127.0.0.1', ttl: 123 }\n      ]\n    })\n\n    socket.send(response, port, address)\n  })\n\n  socket.bind(0, () => {\n    const resolver = new Resolver()\n    resolver.setServers([`127.0.0.1:${socket.address().port}`])\n\n    cb(null, { dnsServer: socket, lookup: _lookup.bind(null, resolver) })\n  })\n}\n\ntest('with autoSelectFamily enable the request succeeds when using request', { skip }, async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const agent = new Agent({ connect: { lookup }, autoSelectFamily: true })\n\n      request(\n        `http://example.org:${server.address().port}/`, {\n          method: 'GET',\n          dispatcher: agent\n        }, (err, { statusCode, body }) => {\n          p.ifError(err)\n\n          let response = Buffer.alloc(0)\n\n          body.on('data', chunk => {\n            response = Buffer.concat([response, chunk])\n          })\n\n          body.on('end', () => {\n            p.strictEqual(statusCode, 200)\n            p.strictEqual(response.toString('utf-8'), 'hello')\n          })\n        })\n    })\n  })\n\n  await p.completed\n})\n\ntest('with autoSelectFamily enable the request succeeds when using a client', { skip }, async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const client = new Client(`http://example.org:${server.address().port}`, { connect: { lookup }, autoSelectFamily: true })\n\n      t.after(client.destroy.bind(client))\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { statusCode, body }) => {\n        p.ifError(err)\n\n        let response = Buffer.alloc(0)\n\n        body.on('data', chunk => {\n          response = Buffer.concat([response, chunk])\n        })\n\n        body.on('end', () => {\n          p.strictEqual(statusCode, 200)\n          p.strictEqual(response.toString('utf-8'), 'hello')\n        })\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('with autoSelectFamily disabled the request fails when using request', { skip }, async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const agent = new Agent({ connect: { lookup, autoSelectFamily: false } })\n\n      request(`http://example.org:${server.address().port}`, {\n        method: 'GET',\n        dispatcher: agent\n      }, (err, { statusCode, body }) => {\n        p.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('with autoSelectFamily disabled via Agent.Options[\"autoSelectFamily\"] the request fails when using request', { skip }, async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const agent = new Agent({ autoSelectFamily: false, connect: { lookup } })\n\n      request(`http://example.org:${server.address().port}`, {\n        method: 'GET',\n        dispatcher: agent\n      }, (err, { statusCode, body }) => {\n        p.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('with autoSelectFamily disabled the request fails when using a client', { skip }, async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const client = new Client(`http://example.org:${server.address().port}`, { connect: { lookup, autoSelectFamily: false } })\n      t.after(client.destroy.bind(client))\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { statusCode, body }) => {\n        p.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('with autoSelectFamily disabled via Client.Options[\"autoSelectFamily\"] the request fails when using a client', { skip }, async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  createDnsServer('::1', '127.0.0.1', function (_, { dnsServer, lookup }) {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    t.after(() => {\n      server.close()\n      dnsServer.close()\n    })\n\n    server.listen(0, '127.0.0.1', () => {\n      const client = new Client(`http://example.org:${server.address().port}`, { autoSelectFamily: false, connect: { lookup } })\n      t.after(client.destroy.bind(client))\n\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { statusCode, body }) => {\n        p.ok(['ECONNREFUSED', 'EAFNOSUPPORT'].includes(err.code))\n      })\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/balanced-pool.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert/strict')\nconst { BalancedPool, Pool, Client, errors } = require('../..')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { tspl } = require('@matteo.collina/tspl')\n\ntest('throws when factory is not a function', (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  try {\n    new BalancedPool(null, { factory: '' }) // eslint-disable-line\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'factory must be a function.')\n  }\n})\n\ntest('add/remove upstreams', (t) => {\n  const p = tspl(t, { plan: 7 })\n\n  const upstream01 = 'http://localhost:1'\n  const upstream02 = 'http://localhost:2'\n\n  const pool = new BalancedPool()\n  p.deepStrictEqual(pool.upstreams, [])\n\n  // try to remove non-existent upstream\n  pool.removeUpstream(upstream01)\n  p.deepStrictEqual(pool.upstreams, [])\n\n  pool.addUpstream(upstream01)\n  p.deepStrictEqual(pool.upstreams, [upstream01])\n\n  // try to add the same upstream\n  pool.addUpstream(upstream01)\n  p.deepStrictEqual(pool.upstreams, [upstream01])\n\n  pool.addUpstream(upstream02)\n  p.deepStrictEqual(pool.upstreams, [upstream01, upstream02])\n\n  pool.removeUpstream(upstream02)\n  p.deepStrictEqual(pool.upstreams, [upstream01])\n\n  pool.removeUpstream(upstream01)\n  p.deepStrictEqual(pool.upstreams, [])\n})\n\ntest('basic get', async (t) => {\n  const p = tspl(t, { plan: 16 })\n\n  let server1Called = 0\n  const server1 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    server1Called++\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server1.close.bind(server1))\n\n  await promisify(server1.listen).call(server1, 0)\n\n  let server2Called = 0\n  const server2 = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    server2Called++\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server2.close.bind(server2))\n\n  await promisify(server2.listen).call(server2, 0)\n\n  const client = new BalancedPool()\n  client.addUpstream(`http://localhost:${server1.address().port}`)\n  client.addUpstream(`http://localhost:${server2.address().port}`)\n  t.after(client.destroy.bind(client))\n\n  {\n    const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n    p.strictEqual(statusCode, 200)\n    p.strictEqual(headers['content-type'], 'text/plain')\n    p.strictEqual('hello', await body.text())\n  }\n\n  {\n    const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n    p.strictEqual(statusCode, 200)\n    p.strictEqual(headers['content-type'], 'text/plain')\n    p.strictEqual('hello', await body.text())\n  }\n\n  p.strictEqual(server1Called, 1)\n  p.strictEqual(server2Called, 1)\n\n  p.strictEqual(client.destroyed, false)\n  p.strictEqual(client.closed, false)\n  await client.close()\n  p.strictEqual(client.destroyed, true)\n  p.strictEqual(client.closed, true)\n})\n\ntest('connect/disconnect event(s)', async (t) => {\n  const clients = 2\n\n  const p = tspl(t, { plan: clients * 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      Connection: 'keep-alive',\n      'Keep-Alive': 'timeout=1s'\n    })\n    res.end('ok')\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const pool = new BalancedPool(`http://localhost:${server.address().port}`, {\n      connections: clients,\n      keepAliveTimeoutThreshold: 100\n    })\n    t.after(() => pool.close.bind(pool)())\n\n    pool.on('connect', (origin, [pool, pool2, client]) => {\n      p.ok(client instanceof Client)\n    })\n    pool.on('disconnect', (origin, [pool, pool2, client], error) => {\n      p.ok(client instanceof Client)\n      p.ok(error instanceof errors.InformationalError)\n      p.strictEqual(error.code, 'UND_ERR_INFO')\n    })\n\n    for (let i = 0; i < clients; i++) {\n      pool.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { headers, body }) => {\n        p.ifError(err)\n        body.resume()\n      })\n    }\n  })\n\n  await p.completed\n})\n\ntest('busy', async (t) => {\n  const p = tspl(t, { plan: 8 * 6 + 2 + 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, async () => {\n    const client = new BalancedPool(`http://localhost:${server.address().port}`, {\n      connections: 2,\n      pipelining: 2\n    })\n    client.on('drain', () => {\n      p.ok(1)\n    })\n    client.on('connect', () => {\n      p.ok(1)\n    })\n    t.after(client.destroy.bind(client))\n\n    for (let n = 1; n <= 8; ++n) {\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    }\n  })\n\n  await p.completed\n})\n\ntest('factory option with basic get request', async (t) => {\n  const p = tspl(t, { plan: 12 })\n\n  let factoryCalled = 0\n  const opts = {\n    factory: (origin, opts) => {\n      factoryCalled++\n      return new Pool(origin, opts)\n    }\n  }\n\n  const client = new BalancedPool([], opts)\n\n  let serverCalled = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    serverCalled++\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server.close.bind(server))\n\n  await promisify(server.listen).call(server, 0)\n\n  client.addUpstream(`http://localhost:${server.address().port}`)\n\n  p.deepStrictEqual(client.upstreams, [`http://localhost:${server.address().port}`])\n\n  t.after(client.destroy.bind(client))\n\n  {\n    const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n    p.strictEqual(statusCode, 200)\n    p.strictEqual(headers['content-type'], 'text/plain')\n    p.strictEqual('hello', await body.text())\n  }\n\n  p.strictEqual(serverCalled, 1)\n  p.strictEqual(factoryCalled, 1)\n\n  p.strictEqual(client.destroyed, false)\n  p.strictEqual(client.closed, false)\n  await client.close()\n  p.strictEqual(client.destroyed, true)\n  p.strictEqual(client.closed, true)\n})\n\ntest('throws when upstream is missing', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const pool = new BalancedPool()\n\n  try {\n    await pool.request({ path: '/', method: 'GET' })\n  } catch (e) {\n    p.ok(e instanceof errors.BalancedPoolMissingUpstreamError)\n    p.strictEqual(e.message, 'No upstream has been added to the BalancedPool')\n  }\n})\n\ntest('getUpstream returns the correct Pool for given upstream', (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  const upstream1 = 'http://localhost:3001'\n  const upstream2 = 'http://localhost:3002'\n\n  const pool = new BalancedPool()\n  pool.addUpstream(upstream1)\n  pool.addUpstream(upstream2)\n\n  const pool1 = pool.getUpstream(upstream1)\n  const pool2 = pool.getUpstream(upstream2)\n\n  p.ok(pool1 instanceof Pool)\n  p.ok(pool2 instanceof Pool)\n  const { kUrl } = require('../../lib/core/symbols')\n  p.strictEqual(pool1[kUrl].origin, upstream1)\n  p.strictEqual(pool2[kUrl].origin, upstream2)\n})\n\ntest('getUpstream returns undefined for non-existent upstream', (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const pool = new BalancedPool()\n  pool.addUpstream('http://localhost:3001')\n\n  const result = pool.getUpstream('http://localhost:9999')\n  p.strictEqual(result, undefined)\n})\n\ntest('getUpstream returns undefined for closed/destroyed upstream', (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const upstream = 'http://localhost:3001'\n  const pool = new BalancedPool()\n  pool.addUpstream(upstream)\n\n  const upstreamPool = pool.getUpstream(upstream)\n  p.ok(upstreamPool instanceof Pool)\n\n  upstreamPool.destroy()\n\n  const result = pool.getUpstream(upstream)\n  p.strictEqual(result, undefined)\n})\n\nclass TestServer {\n  constructor ({ config: { server, socketHangup, downOnRequests, socketHangupOnRequests }, onRequest }) {\n    this.config = {\n      downOnRequests: downOnRequests || [],\n      socketHangupOnRequests: socketHangupOnRequests || [],\n      socketHangup\n    }\n    this.name = server\n    // start a server listening to any port available on the host\n    this.port = 0\n    this.iteration = 0\n    this.requestsCount = 0\n    this.onRequest = onRequest\n    this.server = null\n  }\n\n  _shouldHangupOnClient () {\n    if (this.config.socketHangup) {\n      return true\n    }\n    if (this.config.socketHangupOnRequests.includes(this.requestsCount)) {\n      return true\n    }\n\n    return false\n  }\n\n  _shouldStopServer () {\n    if (this.config.upstreamDown === true || this.config.downOnRequests.includes(this.requestsCount)) {\n      return true\n    }\n    return false\n  }\n\n  async prepareForIteration (iteration) {\n    // set current iteration\n    this.iteration = iteration\n\n    if (this._shouldStopServer()) {\n      await this.stop()\n    } else if (!this.isRunning()) {\n      await this.start()\n    }\n  }\n\n  start () {\n    this.server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      if (this._shouldHangupOnClient()) {\n        req.destroy(new Error('(ツ)'))\n        return\n      }\n      this.requestsCount++\n      res.end('server is running!')\n\n      this.onRequest(this)\n    }).listen(this.port)\n\n    this.server.keepAliveTimeout = 2000\n\n    return new Promise((resolve) => {\n      this.server.on('listening', () => {\n      // store the used port to use it again if the server was stopped as part of test and then started again\n        this.port = this.server.address().port\n\n        return resolve()\n      })\n    })\n  }\n\n  isRunning () {\n    return !!this.server.address()\n  }\n\n  stop () {\n    if (!this.isRunning()) {\n      return\n    }\n\n    return new Promise(resolve => {\n      this.server.close(() => resolve())\n    })\n  }\n}\n\nconst cases = [\n\n  // 0\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 7,\n    config: [{ server: 'A' }, { server: 'B' }, { server: 'C' }],\n    expected: ['A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 0,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.34, 0.33, 0.33]\n  },\n\n  // 1\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', downOnRequests: [0] }, { server: 'B' }, { server: 'C' }],\n    expected: ['A/connectionRefused', 'B', 'C', 'B', 'C', 'B', 'C', 'A', 'B', 'C', 'A'],\n    expectedConnectionRefusedErrors: 1,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.32, 0.34, 0.34]\n  },\n\n  // 2\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A' }, { server: 'B', downOnRequests: [0] }, { server: 'C' }],\n    expected: ['A', 'B/connectionRefused', 'C', 'A', 'C', 'A', 'C', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 1,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.34, 0.32, 0.34]\n  },\n\n  // 3\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A' }, { server: 'B', downOnRequests: [0] }, { server: 'C', downOnRequests: [0] }],\n    expected: ['A', 'B/connectionRefused', 'C/connectionRefused', 'A', 'A', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 2,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.35, 0.33, 0.32]\n  },\n\n  // 4\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', downOnRequests: [0] }, { server: 'B', downOnRequests: [0] }, { server: 'C', downOnRequests: [0] }],\n    expected: ['A/connectionRefused', 'B/connectionRefused', 'C/connectionRefused', 'A', 'B', 'C', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 3,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.34, 0.33, 0.33]\n  },\n\n  // 5\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', downOnRequests: [0, 1, 2] }, { server: 'B', downOnRequests: [0, 1, 2] }, { server: 'C', downOnRequests: [0, 1, 2] }],\n    expected: ['A/connectionRefused', 'B/connectionRefused', 'C/connectionRefused', 'A/connectionRefused', 'B/connectionRefused', 'C/connectionRefused', 'A/connectionRefused', 'B/connectionRefused', 'C/connectionRefused', 'A', 'B', 'C', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 9,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.34, 0.33, 0.33]\n  },\n\n  // 6\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', downOnRequests: [0] }, { server: 'B', downOnRequests: [0, 1] }, { server: 'C', downOnRequests: [0] }],\n    expected: ['A/connectionRefused', 'B/connectionRefused', 'C/connectionRefused', 'A', 'B/connectionRefused', 'C', 'A', 'B', 'C', 'A', 'B', 'C', 'A', 'C', 'A', 'C', 'A', 'C', 'A', 'B'],\n    expectedConnectionRefusedErrors: 4,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.36, 0.29, 0.35]\n  },\n\n  // 7\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', socketHangupOnRequests: [1] }, { server: 'B' }, { server: 'C' }],\n    expected: ['A', 'B', 'C', 'A/socketError', 'B', 'C', 'B', 'C', 'B', 'C', 'A'],\n    expectedConnectionRefusedErrors: 0,\n    expectedSocketErrors: 1,\n    expectedRatios: [0.32, 0.34, 0.34]\n  },\n\n  // 8\n\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 7,\n    config: [{ server: 'A' }, { server: 'B' }, { server: 'C' }, { server: 'D' }, { server: 'E' }],\n    expected: ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E'],\n    expectedConnectionRefusedErrors: 0,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.2, 0.2, 0.2, 0.2, 0.2]\n  },\n\n  // 9\n  {\n    iterations: 100,\n    maxWeightPerServer: 100,\n    errorPenalty: 15,\n    config: [{ server: 'A', downOnRequests: [0, 1, 2, 3] }, { server: 'B' }, { server: 'C' }],\n    expected: ['A/connectionRefused', 'B', 'C', 'B', 'C', 'B', 'C', 'A/connectionRefused', 'B', 'C', 'B', 'C', 'A/connectionRefused', 'B', 'C', 'B', 'C', 'A/connectionRefused', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C'],\n    expectedConnectionRefusedErrors: 4,\n    expectedSocketErrors: 0,\n    expectedRatios: [0.18, 0.41, 0.41]\n  }\n\n]\n\ndescribe('weighted round robin', () => {\n  for (const [index, { config, expected, expectedRatios, iterations = 9, expectedConnectionRefusedErrors = 0, expectedSocketErrors = 0, maxWeightPerServer, errorPenalty = 10, skip = false }] of cases.entries()) {\n    test(`case ${index}`, { skip }, async (t) => {\n    // create an array to store successful requests\n      const requestLog = []\n\n      // create instances of the test servers according to the config\n      const servers = config.map((serverConfig) => new TestServer({\n        config: serverConfig,\n        onRequest: (server) => {\n          requestLog.push(server.name)\n        }\n      }))\n      t.after(() => servers.map(server => server.stop()))\n\n      // start all servers to get a port so that we can build the upstream urls to supply them to undici\n      await Promise.all(servers.map(server => server.start()))\n\n      // build upstream urls\n      const urls = servers.map(server => `http://localhost:${server.port}`)\n\n      // add upstreams\n      const client = new BalancedPool(urls[0], { maxWeightPerServer, errorPenalty, keepAliveTimeoutThreshold: 100 })\n      urls.slice(1).map(url => client.addUpstream(url))\n\n      let connectionRefusedErrors = 0\n      let socketErrors = 0\n      for (let i = 0; i < iterations; i++) {\n      // setup test servers for the next iteration\n\n        await Promise.all(servers.map(server => server.prepareForIteration(i)))\n\n        // send a request using undici\n        try {\n          await client.request({ path: '/', method: 'GET' })\n        } catch (e) {\n          const serverWithError =\n          servers.find(server => server.port === e.port) ||\n          servers.find(server => {\n            if (typeof AggregateError === 'function' && e instanceof AggregateError) {\n              return e.errors.some(e => server.port === (e.socket?.remotePort ?? e.port))\n            }\n\n            return server.port === e.socket.remotePort\n          })\n\n          serverWithError.requestsCount++\n\n          if (e.code === 'ECONNREFUSED') {\n            requestLog.push(`${serverWithError.name}/connectionRefused`)\n            connectionRefusedErrors++\n          }\n          if (e.code === 'UND_ERR_SOCKET') {\n            requestLog.push(`${serverWithError.name}/socketError`)\n\n            socketErrors++\n          }\n        }\n      }\n      const totalRequests = servers.reduce((acc, server) => {\n        return acc + server.requestsCount\n      }, 0)\n\n      assert.strictEqual(totalRequests, iterations)\n\n      assert.strictEqual(connectionRefusedErrors, expectedConnectionRefusedErrors)\n      assert.strictEqual(socketErrors, expectedSocketErrors)\n\n      if (expectedRatios) {\n        const ratios = servers.reduce((acc, el) => {\n          acc[el.name] = 0\n          return acc\n        }, {})\n        requestLog.map(el => ratios[el[0]]++)\n\n        assert.deepStrictEqual(Object.keys(ratios).map(k => ratios[k] / iterations), expectedRatios)\n      }\n\n      if (expected) {\n        assert.deepStrictEqual(requestLog.slice(0, expected.length), expected)\n      }\n\n      await client.close()\n    })\n  }\n})\n\ntest('should not be vulnerable to __proto__ pollution via options', async (t) => {\n  const { EventEmitter } = require('node:events')\n\n  let capturedOpts\n\n  // Simulate attacker-controlled options with __proto__ property\n  const attackerOptions = JSON.parse(`\n    {\n      \"__proto__\": {\n        \"polluted\": \"YES\",\n        \"connections\": 1\n      }\n    }\n  `)\n\n  attackerOptions.factory = (origin, opts) => {\n    capturedOpts = opts\n\n    const stub = new EventEmitter()\n    stub.dispatch = () => true\n    stub.close = () => Promise.resolve()\n    stub.destroy = () => Promise.resolve()\n    stub.destroyed = false\n    stub.closed = false\n\n    return stub\n  }\n\n  new BalancedPool(['http://localhost/'], attackerOptions) // eslint-disable-line no-new\n\n  // Verify that the captured options do not have polluted prototype\n  assert.strictEqual(capturedOpts.polluted, undefined, 'polluted property should not exist on options')\n  assert.strictEqual(Object.getPrototypeOf(capturedOpts).polluted, undefined, 'prototype should not be polluted')\n  assert.strictEqual({}.polluted, undefined, 'global Object.prototype should not be polluted')\n})\n"
  },
  {
    "path": "test/node-test/ca-fingerprint.js",
    "content": "'use strict'\n\nconst crypto = require('node:crypto')\nconst https = require('node:https')\nconst { test } = require('node:test')\nconst { Client, buildConnector } = require('../..')\nconst pem = require('@metcoder95/https-pem')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst caFingerprint = getFingerprint(pem.cert.toString()\n  .split('\\n')\n  .slice(1, -1)\n  .map(line => line.trim())\n  .join('')\n)\n\ntest('Validate CA fingerprint with a custom connector', async t => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = https.createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n\n  server.listen(0, function () {\n    const connector = buildConnector({ rejectUnauthorized: false })\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect (opts, cb) {\n        connector(opts, (err, socket) => {\n          if (err) {\n            cb(err)\n          } else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {\n            socket.destroy()\n            cb(new Error('Fingerprint does not match'))\n          } else {\n            cb(null, socket)\n          }\n        })\n      }\n    })\n\n    t.after(() => {\n      client.close()\n      server.close()\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n\n      data.body\n        .resume()\n        .on('end', () => {\n          p.ok(1)\n        })\n    })\n  })\n\n  await p.completed\n})\n\ntest('Bad CA fingerprint with a custom connector', async t => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = https.createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n\n  server.listen(0, function () {\n    const connector = buildConnector({ rejectUnauthorized: false })\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect (opts, cb) {\n        connector(opts, (err, socket) => {\n          if (err) {\n            cb(err)\n          } else if (getIssuerCertificate(socket).fingerprint256 !== 'FO:OB:AR') {\n            socket.destroy()\n            cb(new Error('Fingerprint does not match'))\n          } else {\n            cb(null, socket)\n          }\n        })\n      }\n    })\n\n    t.after(() => {\n      client.close()\n      server.close()\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.strictEqual(err.message, 'Fingerprint does not match')\n      p.strictEqual(data.body, undefined)\n    })\n  })\n\n  await p.completed\n})\n\nfunction getIssuerCertificate (socket) {\n  let certificate = socket.getPeerCertificate(true)\n  while (certificate && Object.keys(certificate).length > 0) {\n    // invalid certificate\n    if (certificate.issuerCertificate == null) {\n      return null\n    }\n\n    // We have reached the root certificate.\n    // In case of self-signed certificates, `issuerCertificate` may be a circular reference.\n    if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {\n      break\n    }\n\n    // continue the loop\n    certificate = certificate.issuerCertificate\n  }\n  return certificate\n}\n\nfunction getFingerprint (content, inputEncoding = 'base64', outputEncoding = 'hex') {\n  const shasum = crypto.createHash('sha256')\n  shasum.update(content, inputEncoding)\n  const res = shasum.digest(outputEncoding)\n  return res.toUpperCase().match(/.{1,2}/g).join(':')\n}\n"
  },
  {
    "path": "test/node-test/client-abort.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Client, errors } = require('../..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { tspl } = require('@matteo.collina/tspl')\n\nclass OnAbortError extends Error {}\n\ntest('aborted response errors', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.once('request', (req, res) => {\n    // TODO: res.write will cause body to emit 'error' twice\n    // due to bug in readable-stream.\n    res.end('asd')\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      body.destroy()\n      body\n        .on('error', err => {\n          p.ok(err instanceof errors.RequestAbortedError)\n        })\n        .on('close', () => {\n          p.ok(1)\n        })\n    })\n  })\n\n  await p.completed\n})\n\ntest('aborted req', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(Buffer.alloc(4 + 1, 'a'))\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({\n      method: 'POST',\n      path: '/',\n      body: new Readable({\n        read () {\n          setImmediate(() => {\n            this.destroy()\n          })\n        }\n      })\n    }, (err) => {\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n  })\n\n  await p.completed\n})\n\ntest('abort', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.dispatch({\n      method: 'GET',\n      path: '/'\n    }, {\n      onConnect (abort) {\n        setImmediate(abort)\n      },\n      onHeaders () {\n        p.ok(0)\n      },\n      onData () {\n        p.ok(0)\n      },\n      onComplete () {\n        p.ok(0)\n      },\n      onError (err) {\n        p.ok(err instanceof errors.RequestAbortedError)\n      }\n    })\n\n    client.on('disconnect', () => {\n      p.ok(1)\n    })\n  })\n\n  await p.completed\n})\n\ntest('abort pipelined', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    t.after(client.destroy.bind(client))\n\n    let counter = 0\n    client.dispatch({\n      method: 'GET',\n      path: '/',\n      blocking: false\n    }, {\n      onConnect (abort) {\n        // This request will be retried\n        if (counter++ === 1) {\n          abort()\n        }\n        p.ok(1)\n      },\n      onHeaders () {\n        p.ok(0)\n      },\n      onData () {\n        p.ok(0)\n      },\n      onComplete () {\n        p.ok(0)\n      },\n      onError (err) {\n        p.ok(err instanceof errors.RequestAbortedError)\n      }\n    })\n\n    client.dispatch({\n      method: 'GET',\n      path: '/',\n      blocking: false\n    }, {\n      onConnect (abort) {\n        abort()\n      },\n      onHeaders () {\n        p.ok(0)\n      },\n      onData () {\n        p.ok(0)\n      },\n      onComplete () {\n        p.ok(0)\n      },\n      onError (err) {\n        p.ok(err instanceof errors.RequestAbortedError)\n      }\n    })\n\n    client.on('disconnect', () => {\n      p.ok(1)\n    })\n  })\n\n  await p.completed\n})\n\ntest('propagate unallowed throws in request.onError', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.dispatch({\n      method: 'GET',\n      path: '/'\n    }, {\n      onConnect (abort) {\n        setImmediate(abort)\n      },\n      onHeaders () {\n        p.ok(0)\n      },\n      onData () {\n        p.ok(0)\n      },\n      onComplete () {\n        p.ok(0)\n      },\n      onError () {\n        throw new OnAbortError('error')\n      }\n    })\n\n    client.on('error', (err) => {\n      p.ok(err instanceof OnAbortError)\n    })\n\n    client.on('disconnect', () => {\n      p.ok(1)\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/client-connect.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Client, errors } = require('../..')\nconst http = require('node:http')\nconst EE = require('node:events')\nconst { kBusy } = require('../../lib/core/symbols')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\ntest('basic connect', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (c) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const signal = new EE()\n    const promise = client.connect({\n      signal,\n      path: '/'\n    })\n    p.strictEqual(signal.listenerCount('abort'), 1)\n    const { socket } = await promise\n    p.strictEqual(signal.listenerCount('abort'), 0)\n\n    let recvData = ''\n    socket.on('data', (d) => {\n      recvData += d\n    })\n\n    socket.on('end', () => {\n      p.strictEqual(recvData.toString(), 'Body')\n    })\n\n    socket.write('Body')\n    socket.end()\n  })\n\n  await p.completed\n})\n\ntest('connect error', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (c) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.destroy()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    try {\n      await client.connect({\n        path: '/'\n      })\n    } catch (err) {\n      p.ok(err)\n    }\n  })\n\n  await p.completed\n})\n\ntest('connect invalid opts', (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const client = new Client('http://localhost:5432')\n\n  client.connect(null, err => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'invalid opts')\n  })\n\n  try {\n    client.connect(null, null)\n    p.ok(0)\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'invalid opts')\n  }\n\n  try {\n    client.connect({ path: '/' }, null)\n    p.ok(0)\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'invalid callback')\n  }\n})\n\ntest('connect wait for empty pipeline', async (t) => {\n  const p = tspl(t, { plan: 7 })\n\n  let canConnect = false\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n    canConnect = true\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    p.strictEqual(canConnect, true)\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    t.after(() => { return client.close() })\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      blocking: false\n    }, (err) => {\n      p.ifError(err)\n    })\n    client.once('connect', () => {\n      process.nextTick(() => {\n        p.strictEqual(client[kBusy], false)\n\n        client.connect({\n          path: '/'\n        }, (err, { socket }) => {\n          p.ifError(err)\n          let recvData = ''\n          socket.on('data', (d) => {\n            recvData += d\n          })\n\n          socket.on('end', () => {\n            p.strictEqual(recvData.toString(), 'Body')\n          })\n\n          socket.write('Body')\n          socket.end()\n        })\n        p.strictEqual(client[kBusy], true)\n\n        client.request({\n          path: '/',\n          method: 'GET'\n        }, (err) => {\n          p.ifError(err)\n        })\n      })\n    })\n  })\n  await p.completed\n})\n\ntest('connect aborted', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, c, firstBodyChunk) => {\n    p.ok(0)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    t.after(() => {\n      client.destroy()\n    })\n\n    const signal = new EE()\n    client.connect({\n      path: '/',\n      signal,\n      opaque: 'asd'\n    }, (err, { opaque }) => {\n      p.strictEqual(opaque, 'asd')\n      p.strictEqual(signal.listenerCount('abort'), 0)\n      p.ok(err instanceof errors.RequestAbortedError)\n    })\n    p.strictEqual(client[kBusy], true)\n    p.strictEqual(signal.listenerCount('abort'), 1)\n    signal.emit('abort')\n\n    client.close(() => {\n      p.ok(1)\n    })\n  })\n\n  await p.completed\n})\n\ntest('basic connect error', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (c) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const _err = new Error()\n    client.connect({\n      path: '/'\n    }, (err, { socket }) => {\n      p.ifError(err)\n      socket.on('error', (err) => {\n        p.strictEqual(err, _err)\n      })\n      throw _err\n    })\n  })\n\n  await p.completed\n})\n\ntest('connect invalid signal', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, c, firstBodyChunk) => {\n    p.ok(0)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.on('disconnect', () => {\n      p.ok(0)\n    })\n\n    client.connect({\n      path: '/',\n      signal: 'error',\n      opaque: 'asd'\n    }, (err, { opaque }) => {\n      p.strictEqual(opaque, 'asd')\n      p.ok(err instanceof errors.InvalidArgumentError)\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/client-dispatch.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert/strict')\nconst http = require('node:http')\nconst https = require('node:https')\nconst { Client, Pool, errors } = require('../..')\nconst stream = require('node:stream')\nconst { createSecureServer } = require('node:http2')\nconst pem = require('@metcoder95/https-pem')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { closeServerAsPromise, closeClientAndServerAsPromise } = require('../utils/node-http')\n\ntest('dispatch invalid opts', (t) => {\n  const p = tspl(t, { plan: 14 })\n\n  const client = new Client('http://localhost:5000')\n\n  try {\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      upgrade: 1\n    }, null)\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'handler must be an object')\n  }\n\n  try {\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      upgrade: 1\n    }, 'asd')\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'handler must be an object')\n  }\n\n  client.dispatch({\n    path: '/',\n    method: 'GET',\n    upgrade: 1\n  }, {\n    onError (err) {\n      p.ok(err instanceof errors.InvalidArgumentError)\n      p.strictEqual(err.message, 'upgrade must be a string')\n    }\n  })\n\n  client.dispatch({\n    path: '/',\n    method: 'GET',\n    headersTimeout: 'asd'\n  }, {\n    onError (err) {\n      p.ok(err instanceof errors.InvalidArgumentError)\n      p.strictEqual(err.message, 'invalid headersTimeout')\n    }\n  })\n\n  client.dispatch({\n    path: '/',\n    method: 'GET',\n    bodyTimeout: 'asd'\n  }, {\n    onError (err) {\n      p.ok(err instanceof errors.InvalidArgumentError)\n      p.strictEqual(err.message, 'invalid bodyTimeout')\n    }\n  })\n\n  client.dispatch({\n    origin: 'another',\n    path: '/',\n    method: 'GET',\n    bodyTimeout: 'asd'\n  }, {\n    onError (err) {\n      p.ok(err instanceof errors.InvalidArgumentError)\n      p.strictEqual(err.message, 'invalid bodyTimeout')\n    }\n  })\n\n  client.dispatch(null, {\n    onError (err) {\n      p.ok(err instanceof errors.InvalidArgumentError)\n      p.strictEqual(err.message, 'opts must be an object.')\n    }\n  })\n})\n\ntest('basic dispatch get', async (t) => {\n  const p = tspl(t, { plan: 11 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    p.strictEqual(undefined, req.headers.foo)\n    p.strictEqual('bar', req.headers.bar)\n    p.strictEqual('', req.headers.baz)\n    p.strictEqual(undefined, req.headers['content-length'])\n    res.end('hello')\n  })\n  t.after(closeServerAsPromise(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar',\n    baz: null\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const bufs = []\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      headers: reqHeaders\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(Array.isArray(headers), true)\n      },\n      onData (buf) {\n        bufs.push(buf)\n      },\n      onComplete (trailers) {\n        p.deepStrictEqual(trailers, [])\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      },\n      onError () {\n        p.ok(0)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('trailers dispatch get', async (t) => {\n  const p = tspl(t, { plan: 12 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    p.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    p.strictEqual(undefined, req.headers.foo)\n    p.strictEqual('bar', req.headers.bar)\n    p.strictEqual(undefined, req.headers['content-length'])\n    res.addTrailers({ 'Content-MD5': 'test' })\n    res.setHeader('Content-Type', 'text/plain')\n    res.setHeader('Trailer', 'Content-MD5')\n    res.end('hello')\n  })\n  t.after(closeServerAsPromise(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const bufs = []\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      headers: reqHeaders\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(Array.isArray(headers), true)\n        {\n          const contentTypeIdx = headers.findIndex(x => x.toString() === 'Content-Type')\n          p.strictEqual(headers[contentTypeIdx + 1].toString(), 'text/plain')\n        }\n      },\n      onData (buf) {\n        bufs.push(buf)\n      },\n      onComplete (trailers) {\n        p.strictEqual(Array.isArray(trailers), true)\n        {\n          const contentMD5Idx = trailers.findIndex(x => x.toString() === 'Content-MD5')\n          p.strictEqual(trailers[contentMD5Idx + 1].toString(), 'test')\n        }\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      },\n      onError () {\n        p.ok(0)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onHeaders error', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const _err = new Error()\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        throw _err\n      },\n      onData (buf) {\n        p.ok(0)\n      },\n      onComplete (trailers) {\n        p.ok(0)\n      },\n      onError (err) {\n        p.strictEqual(err, _err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onComplete error', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const _err = new Error()\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.ok(1)\n      },\n      onData (buf) {\n        p.ok(0)\n      },\n      onComplete (trailers) {\n        throw _err\n      },\n      onError (err) {\n        p.strictEqual(err, _err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onData error', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const _err = new Error()\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.ok(1)\n      },\n      onData (buf) {\n        throw _err\n      },\n      onComplete (trailers) {\n        p.ok(0)\n      },\n      onError (err) {\n        p.strictEqual(err, _err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onConnect error', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    const _err = new Error()\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n        throw _err\n      },\n      onHeaders (statusCode, headers) {\n        p.ok(0)\n      },\n      onData (buf) {\n        p.ok(0)\n      },\n      onComplete (trailers) {\n        p.ok(0)\n      },\n      onError (err) {\n        p.strictEqual(err, _err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('connect call onUpgrade once', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (c) => {\n    p.ok(0)\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    let recvData = ''\n    let count = 0\n    client.dispatch({\n      method: 'CONNECT',\n      path: '/'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.ok(true, 'should not throw')\n      },\n      onUpgrade (statusCode, headers, socket) {\n        p.strictEqual(count++, 0)\n\n        socket.on('data', (d) => {\n          recvData += d\n        })\n\n        socket.on('end', () => {\n          p.strictEqual(recvData.toString(), 'Body')\n        })\n\n        socket.write('Body')\n        socket.end()\n      },\n      onData (buf) {\n        p.ok(0)\n      },\n      onComplete (trailers) {\n        p.ok(0)\n      },\n      onError () {\n        p.ok(0)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onHeaders missing', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onData (buf) {\n        p.ok(0, 'should not throw')\n      },\n      onComplete (trailers) {\n        p.ok(0, 'should not throw')\n      },\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onData missing', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.ok(0, 'should not throw')\n      },\n      onComplete (trailers) {\n        p.ok(0, 'should not throw')\n      },\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onComplete missing', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        p.ok(0)\n      },\n      onData (buf) {\n        p.ok(0)\n      },\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onError missing', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    try {\n      client.dispatch({\n        path: '/',\n        method: 'GET'\n      }, {\n        onConnect () {\n        },\n        onHeaders (statusCode, headers) {\n          p.ok(0)\n        },\n        onData (buf) {\n          p.ok(0)\n        },\n        onComplete (trailers) {\n          p.ok(0)\n        }\n      })\n    } catch (err) {\n      p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n    }\n  })\n\n  await p.completed\n})\n\ntest('dispatch CONNECT onUpgrade missing', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => client.destroy.bind(client)())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      upgrade: 'Websocket'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n      },\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        p.strictEqual(err.message, 'invalid onUpgrade method')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch upgrade onUpgrade missing', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      upgrade: 'Websocket'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n      },\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        p.strictEqual(err.message, 'invalid onUpgrade method')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch pool onError missing', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    try {\n      client.dispatch({\n        path: '/',\n        method: 'GET',\n        upgrade: 1\n      }, {\n      })\n    } catch (err) {\n      p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n      p.strictEqual(err.message, 'upgrade must be a string')\n    }\n  })\n\n  await p.completed\n})\n\ntest('dispatch onBodySent not a function', async (t) => {\n  const p = tspl(t, { plan: 2 })\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onBodySent: '42',\n      onConnect () {},\n      onHeaders () {},\n      onData () {},\n      onError (err) {\n        p.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        p.strictEqual(err.message, 'invalid onBodySent method')\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onBodySent buffer', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n    const body = 'hello 🚀'\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body\n    }, {\n      onBodySent (chunk) {\n        p.strictEqual(chunk.toString(), body)\n      },\n      onRequestSent () {\n        p.ok(1)\n      },\n      onError (err) {\n        throw err\n      },\n      onConnect () {},\n      onHeaders () {},\n      onData () {},\n      onComplete () {\n        p.ok(1)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onBodySent stream', async (t) => {\n  const p = tspl(t, { plan: 8 })\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n  const chunks = ['he', 'llo', 'world', '🚀']\n  const toSendBytes = chunks.reduce((a, b) => a + Buffer.byteLength(b), 0)\n  const body = stream.Readable.from(chunks)\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n    let sentBytes = 0\n    let currentChunk = 0\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body\n    }, {\n      onBodySent (chunk) {\n        p.strictEqual(chunks[currentChunk++], chunk)\n        sentBytes += Buffer.byteLength(chunk)\n      },\n      onRequestSent () {\n        p.ok(1)\n      },\n      onError (err) {\n        throw err\n      },\n      onConnect () {},\n      onHeaders () {},\n      onData () {},\n      onComplete () {\n        p.strictEqual(currentChunk, chunks.length)\n        p.strictEqual(sentBytes, toSendBytes)\n        p.ok(1)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatch onBodySent async-iterable', (t, done) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ad')\n  })\n  t.after(closeServerAsPromise(server))\n  const chunks = ['he', 'llo', 'world', '🚀']\n  const toSendBytes = chunks.reduce((a, b) => a + Buffer.byteLength(b), 0)\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n    let sentBytes = 0\n    let currentChunk = 0\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body: chunks\n    }, {\n      onBodySent (chunk) {\n        assert.strictEqual(chunks[currentChunk++], chunk)\n        sentBytes += Buffer.byteLength(chunk)\n      },\n      onError (err) {\n        throw err\n      },\n      onConnect () {},\n      onHeaders () {},\n      onData () {},\n      onComplete () {\n        assert.strictEqual(currentChunk, chunks.length)\n        assert.strictEqual(sentBytes, toSendBytes)\n        done()\n      }\n    })\n  })\n})\n\ntest('dispatch onBodySent throws error', (t, done) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ended')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n    const body = 'hello'\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body\n    }, {\n      onBodySent (chunk) {\n        throw new Error('fail')\n      },\n      onError (err) {\n        assert.ok(err instanceof Error)\n        assert.strictEqual(err.message, 'fail')\n        done()\n      },\n      onConnect () {},\n      onHeaders () {},\n      onData () {},\n      onComplete () {}\n    })\n  })\n})\n\ntest('dispatches in expected order', async (t) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ended')\n  })\n  t.after(closeServerAsPromise(server))\n\n  const p = tspl(t, { plan: 1 })\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n\n    t.after(() => { return client.close() })\n\n    const dispatches = []\n\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches.push('onConnect')\n      },\n      onBodySent () {\n        dispatches.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches.push('onHeaders')\n      },\n      onData () {\n        dispatches.push('onData')\n      },\n      onComplete () {\n        dispatches.push('onComplete')\n        p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('onResponseStarted is called with interceptor', async (t) => {\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('ended')\n  })\n  t.after(closeServerAsPromise(server))\n\n  const p = tspl(t, { plan: 2 })\n\n  server.listen(0, () => {\n    const pool = new Pool(`http://localhost:${server.address().port}`)\n    const client = pool.compose((dispatch) => (opts, handler) => dispatch(opts, handler))\n\n    t.after(() => { return pool.close() })\n\n    let responseStartedCalled = false\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {},\n      onResponseStarted () {\n        responseStartedCalled = true\n      },\n      onHeaders () {},\n      onData () {},\n      onComplete () {\n        p.strictEqual(responseStartedCalled, true)\n        p.ok(true)\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('dispatches in expected order for http2', async (t) => {\n  const server = createSecureServer(pem)\n  server.on('stream', (stream) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      ':status': 200\n    })\n    stream.end('ended')\n  })\n\n  const p = tspl(t, { plan: 1 })\n\n  server.listen(0, () => {\n    const client = new Pool(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    t.after(closeClientAndServerAsPromise(client, server))\n\n    const dispatches = []\n\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches.push('onConnect')\n      },\n      onBodySent () {\n        dispatches.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches.push('onHeaders')\n      },\n      onData () {\n        dispatches.push('onData')\n      },\n      onComplete () {\n        dispatches.push('onComplete')\n        p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('Issue#3065 - fix bad destroy handling', async (t) => {\n  const p = tspl(t, { plan: 4 })\n  const server = https.createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end('ended')\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      }\n    })\n\n    t.after(closeClientAndServerAsPromise(client, server))\n\n    const dispatches = []\n    const dispatches2 = []\n\n    client.once('disconnect', (...args) => {\n      const [,, err] = args\n      p.strictEqual(err.code, 'UND_ERR_INFO')\n      p.strictEqual(err.message, 'servername changed')\n    })\n\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches.push('onConnect')\n      },\n      onBodySent () {\n        dispatches.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches.push('onHeaders')\n      },\n      onData () {\n        dispatches.push('onData')\n      },\n      onComplete () {\n        dispatches.push('onComplete')\n        p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n\n    client.dispatch({\n      servername: 'google.com',\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches2.push('onConnect')\n      },\n      onBodySent () {\n        dispatches2.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches2.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches2.push('onHeaders')\n      },\n      onData () {\n        dispatches2.push('onData')\n      },\n      onComplete () {\n        dispatches2.push('onComplete')\n        p.deepStrictEqual(dispatches2, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n  })\n\n  await p.completed\n})\n\ntest('Issue#3065 - fix bad destroy handling (h2)', async (t) => {\n  // Due to we handle the session, the request for h2 will fail on servername change\n  const p = tspl(t, { plan: 4 })\n  const server = createSecureServer(pem)\n  server.on('stream', (stream) => {\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      ':status': 200\n    })\n    stream.end('ended')\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      connect: {\n        rejectUnauthorized: false\n      },\n      allowH2: true\n    })\n\n    t.after(closeClientAndServerAsPromise(client, server))\n\n    const dispatches = []\n    const dispatches2 = []\n\n    client.once('disconnect', (...args) => {\n      const [,, err] = args\n      p.strictEqual(err.code, 'UND_ERR_INFO')\n      p.strictEqual(err.message, 'servername changed')\n    })\n\n    client.dispatch({\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches.push('onConnect')\n      },\n      onBodySent () {\n        dispatches.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches.push('onHeaders1')\n      },\n      onData () {\n        dispatches.push('onData')\n      },\n      onComplete () {\n        dispatches.push('onComplete')\n        p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders1', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n\n    client.dispatch({\n      servername: 'google.com',\n      path: '/',\n      method: 'POST',\n      body: 'body'\n    }, {\n      onConnect () {\n        dispatches2.push('onConnect')\n      },\n      onBodySent () {\n        dispatches2.push('onBodySent')\n      },\n      onResponseStarted () {\n        dispatches2.push('onResponseStarted')\n      },\n      onHeaders () {\n        dispatches2.push('onHeaders2')\n      },\n      onData () {\n        dispatches2.push('onData')\n      },\n      onComplete () {\n        dispatches2.push('onComplete')\n        p.deepStrictEqual(dispatches2, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders2', 'onData', 'onComplete'])\n      },\n      onError (err) {\n        p.ifError(err)\n      }\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/client-errors.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst https = require('node:https')\nconst net = require('node:net')\nconst { Readable } = require('node:stream')\nconst { test, after } = require('node:test')\nconst { Client, Pool, errors } = require('../..')\nconst { createServer } = require('node:http')\nconst pem = require('@metcoder95/https-pem')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst { kSocket } = require('../../lib/core/symbols')\nconst { wrapWithAsyncIterable, maybeWrapStream, consts } = require('../utils/async-iterators')\n\nconst { closeServerAsPromise } = require('../utils/node-http')\n\nclass IteratorError extends Error {}\n\ntest('GET errors and reconnect with pipelining 1', async (t) => {\n  const p = tspl(t, { plan: 9 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.once('request', (req, res) => {\n    // first request received, destroying\n    p.ok(1)\n    res.socket.destroy()\n\n    server.once('request', (req, res) => {\n      p.strictEqual('/', req.url)\n      p.strictEqual('GET', req.method)\n      res.setHeader('content-type', 'text/plain')\n      res.end('hello')\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1\n    })\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET', idempotent: false, opaque: 'asd' }, (err, data) => {\n      p.ok(err instanceof Error) // we are expecting an error\n      p.strictEqual(data.opaque, 'asd')\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      p.strictEqual(statusCode, 200)\n      p.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('GET errors and reconnect with pipelining 3', async (t) => {\n  const server = createServer({ joinDuplicateHeaders: true })\n  const requestsThatWillError = 3\n  let requests = 0\n\n  const p = tspl(t, { plan: 6 + requestsThatWillError * 3 })\n\n  server.on('request', (req, res) => {\n    if (requests++ < requestsThatWillError) {\n      // request received, destroying\n      p.ok(1)\n\n      // socket might not be there if it was destroyed by another\n      // pipelined request\n      if (res.socket) {\n        res.socket.destroy()\n      }\n    } else {\n      p.strictEqual('/', req.url)\n      p.strictEqual('GET', req.method)\n      res.setHeader('content-type', 'text/plain')\n      res.end('hello')\n    }\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    t.after(client.destroy.bind(client))\n\n    // all of these will error\n    for (let i = 0; i < 3; i++) {\n      client.request({ path: '/', method: 'GET', idempotent: false, opaque: 'asd' }, (err, data) => {\n        p.ok(err instanceof Error) // we are expecting an error\n        p.strictEqual(data.opaque, 'asd')\n      })\n    }\n\n    // this will be queued up\n    client.request({ path: '/', method: 'GET', idempotent: false }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      p.strictEqual(statusCode, 200)\n      p.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await p.completed\n})\n\nfunction errorAndPipelining (type) {\n  test(`POST with a ${type} that errors and pipelining 1 should reconnect`, async (t) => {\n    const p = tspl(t, { plan: 12 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.once('request', (req, res) => {\n      p.strictEqual('/', req.url)\n      p.strictEqual('POST', req.method)\n      p.strictEqual('42', req.headers['content-length'])\n\n      const bufs = []\n      req.on('data', (buf) => {\n        bufs.push(buf)\n      })\n\n      req.on('aborted', () => {\n        // we will abruptly close the connection here\n        // but this will still end\n        p.strictEqual('a string', Buffer.concat(bufs).toString('utf8'))\n      })\n\n      server.once('request', (req, res) => {\n        p.strictEqual('/', req.url)\n        p.strictEqual('GET', req.method)\n        res.setHeader('content-type', 'text/plain')\n        res.end('hello')\n      })\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          // higher than the length of the string\n          'content-length': 42\n        },\n        opaque: 'asd',\n        body: maybeWrapStream(new Readable({\n          read () {\n            this.push('a string')\n            this.destroy(new Error('kaboom'))\n          }\n        }), type)\n      }, (err, data) => {\n        p.strictEqual(err.message, 'kaboom')\n        p.strictEqual(data.opaque, 'asd')\n      })\n\n      // this will be queued up\n      client.request({ path: '/', method: 'GET', idempotent: false }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    })\n\n    await p.completed\n  })\n}\n\nerrorAndPipelining(consts.STREAM)\nerrorAndPipelining(consts.ASYNC_ITERATOR)\n\nfunction errorAndChunkedEncodingPipelining (type) {\n  test(`POST with chunked encoding, ${type} body that errors and pipelining 1 should reconnect`, async (t) => {\n    const p = tspl(t, { plan: 12 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.once('request', (req, res) => {\n      p.strictEqual('/', req.url)\n      p.strictEqual('POST', req.method)\n      p.strictEqual(req.headers['content-length'], undefined)\n\n      const bufs = []\n      req.on('data', (buf) => {\n        bufs.push(buf)\n      })\n\n      req.on('aborted', () => {\n        // we will abruptly close the connection here\n        // but this will still end\n        p.strictEqual('a string', Buffer.concat(bufs).toString('utf8'))\n      })\n\n      server.once('request', (req, res) => {\n        p.strictEqual('/', req.url)\n        p.strictEqual('GET', req.method)\n        res.setHeader('content-type', 'text/plain')\n        res.end('hello')\n      })\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        opaque: 'asd',\n        body: maybeWrapStream(new Readable({\n          read () {\n            this.push('a string')\n            this.destroy(new Error('kaboom'))\n          }\n        }), type)\n      }, (err, data) => {\n        p.strictEqual(err.message, 'kaboom')\n        p.strictEqual(data.opaque, 'asd')\n      })\n\n      // this will be queued up\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    })\n    await p.completed\n  })\n}\n\nerrorAndChunkedEncodingPipelining(consts.STREAM)\nerrorAndChunkedEncodingPipelining(consts.ASYNC_ITERATOR)\n\ntest('invalid options throws', (t, done) => {\n  try {\n    new Client({ port: 'foobar', protocol: 'https:' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'Invalid URL: port must be a valid integer or a string representation of an integer.')\n  }\n\n  try {\n    new Client(new URL('http://asd:200/somepath')) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid url')\n  }\n\n  try {\n    new Client(new URL('http://asd:200?q=asd')) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid url')\n  }\n\n  try {\n    new Client(new URL('http://asd:200#asd')) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid url')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      socketPath: 1\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid socketPath')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      keepAliveTimeout: 'asd'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid keepAliveTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      localAddress: 123\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'localAddress must be valid string IP address')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      localAddress: 'abcd123'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'localAddress must be valid string IP address')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      keepAliveMaxTimeout: 'asd'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid keepAliveMaxTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      keepAliveMaxTimeout: 0\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid keepAliveMaxTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      keepAliveTimeoutThreshold: 'asd'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid keepAliveTimeoutThreshold')\n  }\n\n  try {\n    new Client({ // eslint-disable-line\n      protocol: 'asd'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'Invalid URL protocol: the URL must start with `http:` or `https:`.')\n  }\n\n  try {\n    new Client({ // eslint-disable-line\n      hostname: 1\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'Invalid URL hostname: the hostname must be a string or null/undefined.')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      maxHeaderSize: 'asd'\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid maxHeaderSize')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      maxHeaderSize: 0\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid maxHeaderSize')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      maxHeaderSize: 0\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid maxHeaderSize')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      maxHeaderSize: -10\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid maxHeaderSize')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { // eslint-disable-line\n      maxHeaderSize: 1.5\n    })\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid maxHeaderSize')\n  }\n\n  try {\n    new Client(1) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'Invalid URL: The URL argument must be a non-null object.')\n  }\n\n  try {\n    const client = new Client(new URL('http://localhost:200'))\n    client.destroy(null, null)\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid callback')\n  }\n\n  try {\n    const client = new Client(new URL('http://localhost:200'))\n    client.close(null, null)\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid callback')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { maxKeepAliveTimeout: 1e3 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { keepAlive: false }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'unsupported keepAlive, use pipelining=0 instead')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { idleTimeout: 30e3 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'unsupported idleTimeout, use keepAliveTimeout instead')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { socketTimeout: 30e3 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'unsupported socketTimeout, use headersTimeout & bodyTimeout instead')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { requestTimeout: 30e3 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'unsupported requestTimeout, use headersTimeout & bodyTimeout instead')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectTimeout: -1 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid connectTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectTimeout: Infinity }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid connectTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectTimeout: 'asd' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'invalid connectTimeout')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connect: 'asd' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connect must be a function or an object')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connect: -1 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connect must be a function or an object')\n  }\n\n  try {\n    new Pool(new URL('http://localhost:200'), { connect: 'asd' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connect must be a function or an object')\n  }\n\n  try {\n    new Pool(new URL('http://localhost:200'), { connect: -1 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connect must be a function or an object')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { maxCachedSessions: -10 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'maxCachedSessions must be a positive integer or zero')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { maxCachedSessions: 'foo' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'maxCachedSessions must be a positive integer or zero')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { maxRequestsPerClient: 'foo' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'maxRequestsPerClient must be a positive number')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { autoSelectFamilyAttemptTimeout: 'foo' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'autoSelectFamilyAttemptTimeout must be a positive number')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { initialWindowSize: 'foo' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'initialWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { initialWindowSize: 0 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'initialWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { initialWindowSize: -1 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'initialWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { initialWindowSize: 1.5 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'initialWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectionWindowSize: 'foo' }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connectionWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectionWindowSize: 0 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connectionWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectionWindowSize: -1 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connectionWindowSize must be a positive integer, greater than 0')\n  }\n\n  try {\n    new Client(new URL('http://localhost:200'), { connectionWindowSize: 1.5 }) // eslint-disable-line\n    assert.ok(0)\n  } catch (err) {\n    assert.ok(err instanceof errors.InvalidArgumentError)\n    assert.strictEqual(err.message, 'connectionWindowSize must be a positive integer, greater than 0')\n  }\n\n  done()\n})\n\ntest('POST which fails should error response', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    req.once('data', () => {\n      res.destroy()\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    function checkError (err) {\n      // Different platforms error with different codes...\n      p.ok(\n        err.code === 'EPIPE' ||\n        err.code === 'ECONNRESET' ||\n        err.code === 'UND_ERR_SOCKET' ||\n        err.message === 'other side closed'\n      )\n    }\n\n    {\n      const body = new Readable({ read () {} })\n      body.push('asd')\n      body.on('error', (err) => {\n        checkError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        body\n      }, (err) => {\n        checkError(err)\n      })\n    }\n\n    {\n      const body = new Readable({ read () {} })\n      body.push('asd')\n      body.on('error', (err) => {\n        checkError(err)\n      })\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          'content-length': 100\n        },\n        body\n      }, (err) => {\n        checkError(err)\n      })\n    }\n\n    {\n      const body = wrapWithAsyncIterable(['asd'], true)\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        body\n      }, (err) => {\n        checkError(err)\n      })\n    }\n\n    {\n      const body = wrapWithAsyncIterable(['asd'], true)\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          'content-length': 100\n        },\n        body\n      }, (err) => {\n        checkError(err)\n      })\n    }\n  })\n\n  await p.completed\n})\n\ntest('client destroy cleanup', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const _err = new Error('kaboom')\n  let client\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.once('request', (req, res) => {\n    req.once('data', () => {\n      client.destroy(_err, (err) => {\n        p.ifError(err)\n      })\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    const body = new Readable({ read () {} })\n    body.push('asd')\n    body.on('error', (err) => {\n      p.strictEqual(err, _err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body\n    }, (err, data) => {\n      p.strictEqual(err, _err)\n    })\n  })\n\n  await p.completed\n})\n\ntest('throwing async-iterator causes error', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end(Buffer.alloc(4 + 1, 'a'))\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({\n      method: 'POST',\n      path: '/',\n      body: (async function * () {\n        yield 'hello'\n        throw new IteratorError('bad iterator')\n      })()\n    }, (err) => {\n      p.ok(err instanceof IteratorError)\n    })\n  })\n\n  await p.completed\n})\n\ntest('client async-iterator destroy cleanup', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const _err = new Error('kaboom')\n  let client\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.once('request', (req, res) => {\n    req.once('data', () => {\n      client.destroy(_err, (err) => {\n        p.ifError(err)\n      })\n    })\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    const body = wrapWithAsyncIterable(['asd'], true)\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body\n    }, (err, data) => {\n      p.strictEqual(err, _err)\n    })\n  })\n\n  await p.completed\n})\n\ntest('GET errors body', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.once('request', (req, res) => {\n    res.write('asd')\n    setTimeout(() => {\n      res.destroy()\n    }, 19)\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      p.ifError(err)\n      body.resume()\n      body.on('error', err => (\n        p.ok(err)\n      ))\n    })\n  })\n\n  await p.completed\n})\n\ntest('validate request body', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(() => { return client.close() })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: /asdasd/\n    }, (err, data) => {\n      p.ok(err instanceof errors.InvalidArgumentError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: 0\n    }, (err, data) => {\n      p.ok(err instanceof errors.InvalidArgumentError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: false\n    }, (err, data) => {\n      p.ok(err instanceof errors.InvalidArgumentError)\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: ''\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: new Uint8Array()\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n    })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: Buffer.alloc(10)\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n    })\n  })\n\n  await p.completed\n})\n\nfunction socketFailWrite (type) {\n  test(`socket fail while writing ${type} request body`, async (t) => {\n    const p = tspl(t, { plan: 2 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.once('request', (req, res) => {\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`)\n      t.after(client.destroy.bind(client))\n\n      const preBody = new Readable({ read () {} })\n      preBody.push('asd')\n      const body = maybeWrapStream(preBody, type)\n      client.on('connect', () => {\n        process.nextTick(() => {\n          client[kSocket].destroy('kaboom')\n        })\n      })\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        body\n      }, (err) => {\n        p.ok(err)\n      })\n      client.close((err) => {\n        p.ifError(err)\n      })\n    })\n\n    await p.completed\n  })\n}\nsocketFailWrite(consts.STREAM)\nsocketFailWrite(consts.ASYNC_ITERATOR)\n\nfunction socketFailEndWrite (type) {\n  test(`socket fail while ending ${type} request body`, async (t) => {\n    const p = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.once('request', (req, res) => {\n      res.end()\n    })\n    t.after(closeServerAsPromise(server))\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        pipelining: 2\n      })\n      t.after(client.destroy.bind(client))\n\n      const _err = new Error('kaboom')\n      client.on('connect', () => {\n        process.nextTick(() => {\n          client[kSocket].destroy(_err)\n        })\n      })\n      const preBody = new Readable({ read () {} })\n      preBody.push(null)\n      const body = maybeWrapStream(preBody, type)\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        body\n      }, (err) => {\n        p.strictEqual(err, _err)\n      })\n      client.close((err) => {\n        p.ifError(err)\n        client.close((err) => {\n          p.ok(err instanceof errors.ClientDestroyedError)\n        })\n      })\n    })\n\n    await p.completed\n  })\n}\n\nsocketFailEndWrite(consts.STREAM)\nsocketFailEndWrite(consts.ASYNC_ITERATOR)\n\ntest('queued request should not fail on socket destroy', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1\n    })\n    t.after(client.destroy.bind(client))\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume().on('error', () => {\n        p.ok(1)\n      })\n      client[kSocket].destroy()\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        p.ifError(err)\n        data.body.resume().on('end', () => {\n          p.ok(1)\n        })\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('queued request should fail on client destroy', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1\n    })\n    t.after(client.destroy.bind(client))\n\n    let requestErrored = false\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n        .on('error', () => {\n          p.ok(1)\n        })\n      client.destroy((err) => {\n        p.ifError(err)\n        p.strictEqual(requestErrored, true)\n      })\n    })\n    client.request({\n      path: '/',\n      method: 'GET',\n      opaque: 'asd'\n    }, (err, data) => {\n      requestErrored = true\n      p.ok(err)\n      p.strictEqual(data.opaque, 'asd')\n    })\n  })\n\n  await p.completed\n})\n\ntest('retry idempotent inflight', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    t.after(() => { return client.close() })\n\n    client.request({\n      path: '/',\n      method: 'POST',\n      body: new Readable({\n        read () {\n          this.destroy(new Error('kaboom'))\n        }\n      })\n    }, (err) => {\n      p.ok(err)\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n    })\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n      data.body.resume()\n    })\n  })\n\n  await p.completed\n})\n\ntest('invalid opts', async (t) => {\n  const p = tspl(t, { plan: 5 })\n\n  const client = new Client('http://localhost:5000')\n  client.request(null, (err) => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n  })\n  client.pipeline(null).on('error', (err) => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n  })\n  client.request({\n    path: '/',\n    method: 'GET',\n    highWaterMark: '1000'\n  }, (err) => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'invalid highWaterMark')\n  })\n  client.request({\n    path: '/',\n    method: 'GET',\n    highWaterMark: -1\n  }, (err) => {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'invalid highWaterMark')\n  })\n\n  await p.completed\n})\n\ntest('default port for http and https', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  try {\n    new Client(new URL('http://localhost:80')) // eslint-disable-line\n    p.ok('Should not throw')\n  } catch (err) {\n    p.fail(err)\n  }\n\n  try {\n    new Client(new URL('http://localhost')) // eslint-disable-line\n    p.ok('Should not throw')\n  } catch (err) {\n    p.fail(err)\n  }\n\n  try {\n    new Client(new URL('https://localhost:443')) // eslint-disable-line\n    p.ok('Should not throw')\n  } catch (err) {\n    p.fail(err)\n  }\n\n  try {\n    new Client(new URL('https://localhost')) // eslint-disable-line\n    p.ok('Should not throw')\n  } catch (err) {\n    p.fail(err)\n  }\n})\n\ntest('CONNECT throws in next tick', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      p.ifError(err)\n      data.body\n        .on('end', () => {\n          let ticked = false\n          client.request({\n            path: '/',\n            method: 'CONNECT'\n          }, (err) => {\n            p.ok(err)\n            p.strictEqual(ticked, true)\n          })\n          ticked = true\n        })\n        .resume()\n    })\n  })\n\n  await p.completed\n})\n\ntest('invalid signal', async (t) => {\n  const p = tspl(t, { plan: 8 })\n\n  const client = new Client('http://localhost:3333')\n  t.after(client.destroy.bind(client))\n\n  let ticked = false\n  client.request({ path: '/', method: 'GET', signal: {}, opaque: 'asd' }, (err, { opaque }) => {\n    p.strictEqual(ticked, true)\n    p.strictEqual(opaque, 'asd')\n    p.ok(err instanceof errors.InvalidArgumentError)\n  })\n  client.pipeline({ path: '/', method: 'GET', signal: {} }, () => {})\n    .on('error', (err) => {\n      p.strictEqual(ticked, true)\n      p.ok(err instanceof errors.InvalidArgumentError)\n    })\n  client.stream({ path: '/', method: 'GET', signal: {}, opaque: 'asd' }, () => {}, (err, { opaque }) => {\n    p.strictEqual(ticked, true)\n    p.strictEqual(opaque, 'asd')\n    p.ok(err instanceof errors.InvalidArgumentError)\n  })\n  ticked = true\n\n  await p.completed\n})\n\ntest('invalid body chunk does not crash', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({\n      path: '/',\n      body: new Readable({\n        objectMode: true,\n        read () {\n          this.push({})\n        }\n      }),\n      method: 'GET'\n    }, (err) => {\n      p.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE')\n    })\n  })\n\n  await p.completed\n})\n\ntest('socket errors', async (t) => {\n  const p = tspl(t, { plan: 2 })\n  const client = new Client('http://localhost:5554')\n  t.after(client.destroy.bind(client))\n\n  client.request({ path: '/', method: 'GET' }, (err, data) => {\n    p.ok(err)\n    // TODO: Why UND_ERR_SOCKET?\n    p.ok(err.code === 'ECONNREFUSED' || err.code === 'UND_ERR_SOCKET', err.code)\n    p.end()\n  })\n\n  await p.completed\n})\n\ntest('headers overflow', (t, done) => {\n  const p = tspl(t, { plan: 2 })\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.writeHead(200, {\n      'x-test-1': '1',\n      'x-test-2': '2'\n    })\n    res.end()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      maxHeaderSize: 10\n    })\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ok(err)\n      p.strictEqual(err.code, 'UND_ERR_HEADERS_OVERFLOW')\n      done()\n    })\n  })\n})\n\ntest('SocketError should expose socket details (net)', async (t) => {\n  const p = tspl(t, { plan: 8 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.once('request', (req, res) => {\n    res.destroy()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ok(err instanceof errors.SocketError)\n      if (err.socket.remoteFamily === 'IPv4') {\n        p.strictEqual(err.socket.remoteFamily, 'IPv4')\n        p.strictEqual(err.socket.localAddress, '127.0.0.1')\n        p.strictEqual(err.socket.remoteAddress, '127.0.0.1')\n      } else {\n        p.strictEqual(err.socket.remoteFamily, 'IPv6')\n        p.strictEqual(err.socket.localAddress, '::1')\n        p.strictEqual(err.socket.remoteAddress, '::1')\n      }\n      p.ok(typeof err.socket.localPort === 'number')\n      p.ok(typeof err.socket.remotePort === 'number')\n      p.ok(typeof err.socket.bytesWritten === 'number')\n      p.ok(typeof err.socket.bytesRead === 'number')\n    })\n  })\n  await p.completed\n})\n\ntest('SocketError should expose socket details (tls)', async (t) => {\n  const p = tspl(t, { plan: 8 })\n\n  const server = https.createServer({ ...pem, joinDuplicateHeaders: true })\n\n  server.once('request', (req, res) => {\n    res.destroy()\n  })\n  t.after(closeServerAsPromise(server))\n\n  server.listen(0, () => {\n    const client = new Client(`https://localhost:${server.address().port}`, {\n      tls: {\n        rejectUnauthorized: false\n      }\n    })\n    t.after(client.destroy.bind(client))\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ok(err instanceof errors.SocketError)\n      if (err.socket.remoteFamily === 'IPv4') {\n        p.strictEqual(err.socket.remoteFamily, 'IPv4')\n        p.strictEqual(err.socket.localAddress, '127.0.0.1')\n        p.strictEqual(err.socket.remoteAddress, '127.0.0.1')\n      } else {\n        p.strictEqual(err.socket.remoteFamily, 'IPv6')\n        p.strictEqual(err.socket.localAddress, '::1')\n        p.strictEqual(err.socket.remoteAddress, '::1')\n      }\n      p.ok(typeof err.socket.localPort === 'number')\n      p.ok(typeof err.socket.remotePort === 'number')\n      p.ok(typeof err.socket.bytesWritten === 'number')\n      p.ok(typeof err.socket.bytesRead === 'number')\n    })\n  })\n\n  await p.completed\n})\n\ntest('parser error', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true })\n  server.once('connection', (socket) => {\n    socket.write('asd\\n\\r213123')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err) => {\n      t.ok(err)\n      client.close((err) => {\n        t.ifError(err)\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/node-test/debug.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { spawn } = require('node:child_process')\nconst { join } = require('node:path')\nconst { tspl } = require('@matteo.collina/tspl')\n\n// eslint-disable-next-line no-control-regex\nconst removeEscapeColorsRE = /[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g\n\nconst isNode23Plus = process.versions.node.split('.')[0] >= 23\nconst isCITGM = !!process.env.CITGM\n\ntest('debug#websocket', { skip: !process.versions.icu || isCITGM || isNode23Plus }, async t => {\n  const assert = tspl(t, { plan: 6 })\n  const child = spawn(\n    process.execPath,\n    [\n      '--no-experimental-fetch',\n      join(__dirname, '../fixtures/websocket.js')],\n    {\n      env: {\n        NODE_DEBUG: 'websocket'\n      }\n    }\n  )\n  const chunks = []\n  const assertions = [\n    /(WEBSOCKET [0-9]+:) (connecting to)/,\n    /(WEBSOCKET [0-9]+:) (connected to)/,\n    /(WEBSOCKET [0-9]+:) (sending request)/,\n    /(WEBSOCKET [0-9]+:) (connection opened)/,\n    /(WEBSOCKET [0-9]+:) (closed connection to)/,\n    /^$/\n  ]\n\n  child.stderr.setEncoding('utf8')\n  child.stderr.on('data', chunk => {\n    chunks.push(chunk)\n  })\n  child.stderr.on('end', () => {\n    const lines = extractLines(chunks)\n    assert.strictEqual(lines.length, assertions.length)\n    for (let i = 1; i < lines.length; i++) {\n      assert.match(lines[i], assertions[i])\n    }\n  })\n\n  await assert.completed\n})\n\ntest('debug#fetch', { skip: isCITGM || isNode23Plus }, async t => {\n  const assert = tspl(t, { plan: 7 })\n  const child = spawn(\n    process.execPath,\n    [\n      '--no-experimental-fetch',\n      join(__dirname, '../fixtures/fetch.js')\n    ],\n    {\n      env: Object.assign({}, process.env, { NODE_DEBUG: 'fetch' })\n    }\n  )\n  const chunks = []\n  const assertions = [\n    /(FETCH [0-9]+:) (connecting to)/,\n    /(FETCH [0-9]+:) (connected to)/,\n    /(FETCH [0-9]+:) (sending request)/,\n    /(FETCH [0-9]+:) (received response)/,\n    /(FETCH [0-9]+:) (trailers received)/,\n    /^$/\n  ]\n\n  child.stderr.setEncoding('utf8')\n  child.stderr.on('data', chunk => {\n    chunks.push(chunk)\n  })\n  child.stderr.on('end', () => {\n    const lines = extractLines(chunks)\n    assert.strictEqual(lines.length, assertions.length)\n    for (let i = 0; i < lines.length; i++) {\n      assert.match(lines[i], assertions[i])\n    }\n  })\n\n  await assert.completed\n})\n\ntest('debug#undici', { skip: isCITGM || isNode23Plus }, async t => {\n  // Due to Node.js webpage redirect\n  const assert = tspl(t, { plan: 7 })\n  const child = spawn(\n    process.execPath,\n    [\n      '--no-experimental-fetch',\n      join(__dirname, '../fixtures/undici.js')\n    ],\n    {\n      env: {\n        NODE_DEBUG: 'undici'\n      }\n    }\n  )\n  const chunks = []\n  const assertions = [\n    /(UNDICI [0-9]+:) (connecting to)/,\n    /(UNDICI [0-9]+:) (connected to)/,\n    /(UNDICI [0-9]+:) (sending request)/,\n    /(UNDICI [0-9]+:) (received response)/,\n    /(UNDICI [0-9]+:) (trailers received)/,\n    /^$/\n  ]\n\n  child.stderr.setEncoding('utf8')\n  child.stderr.on('data', chunk => {\n    chunks.push(chunk)\n  })\n  child.stderr.on('end', () => {\n    const lines = extractLines(chunks)\n    assert.strictEqual(lines.length, assertions.length)\n    for (let i = 0; i < lines.length; i++) {\n      assert.match(lines[i], assertions[i])\n    }\n  })\n\n  await assert.completed\n})\n\ntest('debug#undici no duplicates', { skip: isCITGM || isNode23Plus }, async t => {\n  const assert = tspl(t, { plan: 7 })\n  const child = spawn(\n    process.execPath,\n    [\n      '--no-experimental-fetch',\n      join(__dirname, '../fixtures/duplicate-debug.js')\n    ],\n    {\n      env: {\n        NODE_DEBUG: 'undici'\n      }\n    }\n  )\n  const chunks = []\n  const assertions = [\n    /(UNDICI [0-9]+:) (connecting to)/,\n    /(UNDICI [0-9]+:) (connected to)/,\n    /(UNDICI [0-9]+:) (sending request)/,\n    /(UNDICI [0-9]+:) (received response)/,\n    /(UNDICI [0-9]+:) (trailers received)/,\n    /^$/\n  ]\n\n  child.stderr.setEncoding('utf8')\n  child.stderr.on('data', chunk => {\n    chunks.push(chunk)\n  })\n  child.stderr.on('end', () => {\n    const lines = extractLines(chunks)\n    // Should have exactly the expected number of lines, no duplicates\n    assert.strictEqual(lines.length, assertions.length, 'Should not have duplicate log lines')\n    for (let i = 0; i < lines.length; i++) {\n      assert.match(lines[i], assertions[i])\n    }\n  })\n\n  await assert.completed\n})\n\nfunction extractLines (chunks) {\n  return chunks\n    .join('')\n    .split('\\n')\n    .map(v => v.replace(removeEscapeColorsRE, ''))\n}\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/connect-error.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client } = require('../../..')\n\ntest('Diagnostics channel - connect error', (t) => {\n  const connectError = new Error('custom error')\n  const assert = tspl(t, { plan: 16 })\n\n  let _connector\n  diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {\n    _connector = connector\n\n    assert.equal(typeof _connector, 'function')\n    assert.equal(Object.keys(connectParams).length, 7)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, 'localhost:1234')\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, '1234')\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  diagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, connectParams, connector }) => {\n    assert.equal(Object.keys(connectParams).length, 7)\n    assert.equal(_connector, connector)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(error, connectError)\n    assert.equal(host, 'localhost:1234')\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, '1234')\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  const client = new Client('http://localhost:1234', {\n    connect: (_, cb) => { cb(connectError, null) }\n  })\n\n  return new Promise((resolve) => {\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      assert.equal(err, connectError)\n      client.close()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/error.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client } = require('../../..')\nconst { createServer } = require('node:http')\n\ntest('Diagnostics channel - error', (t) => {\n  const assert = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n  after(server.close.bind(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  let _req\n  diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n    _req = request\n  })\n\n  diagnosticsChannel.channel('undici:request:error').subscribe(({ request, error }) => {\n    assert.equal(_req, request)\n    assert.equal(error.code, 'UND_ERR_SOCKET')\n  })\n\n  return new Promise((resolve) => {\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        keepAliveTimeout: 300e3\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        headers: reqHeaders\n      }, (err, data) => {\n        assert.equal(err.code, 'UND_ERR_SOCKET')\n        client.close()\n        resolve()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/get-h2.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createSecureServer } = require('node:http2')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { once } = require('node:events')\nconst pem = require('@metcoder95/https-pem')\nconst { Client } = require('../../..')\n\ntest('Diagnostics channel - get support H2', async t => {\n  const server = createSecureServer(pem)\n\n  server.on('stream', (stream, headers, _flags, rawHeaders) => {\n    t.strictEqual(headers['x-my-header'], 'foo')\n    t.strictEqual(headers[':method'], 'GET')\n    stream.respond({\n      'content-type': 'text/plain; charset=utf-8',\n      'x-custom-h2': 'hello',\n      ':status': 200\n    })\n    stream.end('hello h2!')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n    t.strictEqual(request.origin, `https://localhost:${server.address().port}`)\n    t.strictEqual(request.completed, false)\n    t.strictEqual(request.method, 'GET')\n    t.strictEqual(request.path, '/')\n  })\n\n  let _socket\n  diagnosticsChannel.channel('undici:client:connected').subscribe(({ socket }) => {\n    _socket = socket\n  })\n\n  diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ headers, socket }) => {\n    t.strictEqual(_socket, socket)\n    const expectedHeaders = [\n      'x-my-header: foo',\n      `:authority: localhost:${server.address().port}`,\n      ':method: GET',\n      ':path: /',\n      ':scheme: https'\n    ]\n    t.strictEqual(headers, expectedHeaders.join('\\r\\n') + '\\r\\n')\n  })\n\n  const client = new Client(`https://localhost:${server.address().port}`, {\n    connect: {\n      rejectUnauthorized: false\n    },\n    allowH2: true\n  })\n\n  t = tspl(t, { plan: 24 })\n  after(() => server.close())\n  after(() => client.close())\n\n  let body = []\n  let response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n\n  // request again\n  body = []\n  response = await client.request({\n    path: '/',\n    method: 'GET',\n    headers: {\n      'x-my-header': 'foo'\n    }\n  })\n\n  response.body.on('data', chunk => {\n    body.push(chunk)\n  })\n\n  await once(response.body, 'end')\n  t.strictEqual(response.statusCode, 200)\n  t.strictEqual(response.headers['content-type'], 'text/plain; charset=utf-8')\n  t.strictEqual(response.headers['x-custom-h2'], 'hello')\n  t.strictEqual(Buffer.concat(body).toString('utf8'), 'hello h2!')\n})\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/get.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client } = require('../../..')\nconst { createServer } = require('node:http')\n\ntest('Diagnostics channel - get', (t) => {\n  const assert = tspl(t, { plan: 36 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('Content-Type', 'text/plain')\n    res.setHeader('trailer', 'foo')\n    res.write('hello')\n    res.addTrailers({\n      foo: 'oof'\n    })\n    res.end()\n  })\n\n  after(server.close.bind(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  let _req\n  diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n    _req = request\n    assert.equal(request.origin, `http://localhost:${server.address().port}`)\n    assert.equal(request.completed, false)\n    assert.equal(request.method, 'GET')\n    assert.equal(request.path, '/')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar'])\n    request.addHeader('hello', 'world')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar', 'hello', 'world'])\n  })\n\n  let _connector\n  diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {\n    _connector = connector\n\n    assert.equal(typeof _connector, 'function')\n    assert.equal(Object.keys(connectParams).length, 7)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  let _socket\n  diagnosticsChannel.channel('undici:client:connected').subscribe(({ connectParams, socket, connector }) => {\n    _socket = socket\n\n    assert.equal(_connector, connector)\n    assert.equal(Object.keys(connectParams).length, 7)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ request, headers, socket }) => {\n    assert.equal(_req, request)\n    assert.equal(_socket, socket)\n\n    const expectedHeaders = [\n      'GET / HTTP/1.1',\n      `host: localhost:${server.address().port}`,\n      'connection: keep-alive',\n      'bar: bar',\n      'hello: world'\n    ]\n\n    assert.deepStrictEqual(headers, expectedHeaders.join('\\r\\n') + '\\r\\n')\n  })\n\n  diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => {\n    assert.equal(_req, request)\n    assert.equal(response.statusCode, 200)\n    const expectedHeaders = [\n      Buffer.from('Content-Type'),\n      Buffer.from('text/plain'),\n      Buffer.from('trailer'),\n      Buffer.from('foo'),\n      Buffer.from('Date'),\n      response.headers[5], // This is a date\n      Buffer.from('Connection'),\n      Buffer.from('keep-alive'),\n      Buffer.from('Keep-Alive'),\n      Buffer.from('timeout=5'),\n      Buffer.from('Transfer-Encoding'),\n      Buffer.from('chunked')\n    ]\n    assert.deepStrictEqual(response.headers, expectedHeaders)\n    assert.equal(response.statusText, 'OK')\n  })\n\n  let bodySent = false\n  diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {\n    assert.equal(_req, request)\n    bodySent = true\n  })\n  diagnosticsChannel.channel('undici:request:bodyChunkSent').subscribe(() => {\n    assert.fail('should not emit undici:request:bodyChunkSent for GET requests')\n  })\n\n  let endEmitted = false\n\n  return new Promise((resolve) => {\n    const respChunks = []\n    diagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ request, chunk }) => {\n      assert.equal(_req, request)\n      respChunks.push(chunk)\n    })\n\n    diagnosticsChannel.channel('undici:request:trailers').subscribe(({ request, trailers }) => {\n      assert.equal(bodySent, true)\n      assert.equal(request.completed, true)\n      assert.equal(_req, request)\n      // This event is emitted after the last chunk has been added to the body stream,\n      // not when it was consumed by the application\n      assert.equal(endEmitted, false)\n      assert.deepStrictEqual(trailers, [Buffer.from('foo'), Buffer.from('oof')])\n\n      const respData = Buffer.concat(respChunks)\n      assert.deepStrictEqual(respData, Buffer.from('hello'))\n\n      resolve()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        keepAliveTimeout: 300e3\n      })\n\n      client.request({\n        path: '/',\n        method: 'GET',\n        headers: reqHeaders\n      }, (err, data) => {\n        assert.ok(!err)\n        client.close()\n\n        data.body.on('end', function () {\n          endEmitted = true\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/post-stream.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { Readable } = require('node:stream')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client } = require('../../..')\nconst { createServer } = require('node:http')\n\ntest('Diagnostics channel - post stream', (t) => {\n  const assert = tspl(t, { plan: 43 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.resume()\n    res.setHeader('Content-Type', 'text/plain')\n    res.setHeader('trailer', 'foo')\n    res.write('hello')\n    res.addTrailers({\n      foo: 'oof'\n    })\n    res.end()\n  })\n  after(server.close.bind(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n  const body = Readable.from(['hello', ' ', 'world'])\n\n  let _req\n  diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n    _req = request\n    assert.equal(request.completed, false)\n    assert.equal(request.method, 'POST')\n    assert.equal(request.path, '/')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar'])\n    request.addHeader('hello', 'world')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar', 'hello', 'world'])\n    assert.deepStrictEqual(request.body, body)\n  })\n\n  let _connector\n  diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {\n    _connector = connector\n\n    assert.equal(typeof _connector, 'function')\n    assert.equal(Object.keys(connectParams).length, 7)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  let _socket\n  diagnosticsChannel.channel('undici:client:connected').subscribe(({ connectParams, socket, connector }) => {\n    _socket = socket\n\n    assert.equal(Object.keys(connectParams).length, 7)\n    assert.equal(_connector, connector)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ request, headers, socket }) => {\n    assert.equal(_req, request)\n    assert.equal(_socket, socket)\n\n    const expectedHeaders = [\n      'POST / HTTP/1.1',\n      `host: localhost:${server.address().port}`,\n      'connection: keep-alive',\n      'bar: bar',\n      'hello: world'\n    ]\n\n    assert.equal(headers, expectedHeaders.join('\\r\\n') + '\\r\\n')\n  })\n\n  diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => {\n    assert.equal(_req, request)\n    assert.equal(response.statusCode, 200)\n    const expectedHeaders = [\n      Buffer.from('Content-Type'),\n      Buffer.from('text/plain'),\n      Buffer.from('trailer'),\n      Buffer.from('foo'),\n      Buffer.from('Date'),\n      response.headers[5], // This is a date\n      Buffer.from('Connection'),\n      Buffer.from('keep-alive'),\n      Buffer.from('Keep-Alive'),\n      Buffer.from('timeout=5'),\n      Buffer.from('Transfer-Encoding'),\n      Buffer.from('chunked')\n    ]\n    assert.deepStrictEqual(response.headers, expectedHeaders)\n    assert.equal(response.statusText, 'OK')\n  })\n\n  let bodySent = false\n  const bodyChunks = []\n  diagnosticsChannel.channel('undici:request:bodyChunkSent').subscribe(({ request, chunk }) => {\n    assert.equal(_req, request)\n    // Chunk can be a string or a Buffer, depending on the stream writer.\n    assert.equal(typeof chunk, 'string')\n    bodyChunks.push(Buffer.from(chunk))\n  })\n  diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {\n    assert.equal(_req, request)\n    bodySent = true\n\n    const requestBody = Buffer.concat(bodyChunks)\n    assert.deepStrictEqual(requestBody, Buffer.from('hello world'))\n  })\n\n  let endEmitted = false\n\n  return new Promise((resolve) => {\n    const respChunks = []\n    diagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ request, chunk }) => {\n      assert.equal(_req, request)\n      respChunks.push(chunk)\n    })\n\n    diagnosticsChannel.channel('undici:request:trailers').subscribe(({ request, trailers }) => {\n      assert.equal(bodySent, true)\n      assert.equal(request.completed, true)\n      assert.equal(_req, request)\n      // This event is emitted after the last chunk has been added to the body stream,\n      // not when it was consumed by the application\n      assert.equal(endEmitted, false)\n      assert.deepStrictEqual(trailers, [Buffer.from('foo'), Buffer.from('oof')])\n\n      const respData = Buffer.concat(respChunks)\n      assert.deepStrictEqual(respData, Buffer.from('hello'))\n\n      resolve()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        keepAliveTimeout: 300e3\n      })\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: reqHeaders,\n        body\n      }, (err, data) => {\n        assert.ok(!err)\n        client.close()\n        data.body.on('end', function () {\n          endEmitted = true\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-test/diagnostics-channel/post.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { Client } = require('../../../')\nconst { createServer } = require('node:http')\n\ntest('Diagnostics channel - post', (t) => {\n  const assert = tspl(t, { plan: 39 })\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    req.resume()\n    res.setHeader('Content-Type', 'text/plain')\n    res.setHeader('trailer', 'foo')\n    res.write('hello')\n    res.addTrailers({\n      foo: 'oof'\n    })\n    res.end()\n  })\n  after(server.close.bind(server))\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  let _req\n  diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {\n    _req = request\n    assert.equal(request.completed, false)\n    assert.equal(request.method, 'POST')\n    assert.equal(request.path, '/')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar'])\n    request.addHeader('hello', 'world')\n    assert.deepStrictEqual(request.headers, ['bar', 'bar', 'hello', 'world'])\n    assert.deepStrictEqual(request.body, Buffer.from('hello world'))\n  })\n\n  let _connector\n  diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {\n    _connector = connector\n\n    assert.equal(typeof _connector, 'function')\n    assert.equal(Object.keys(connectParams).length, 7)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  let _socket\n  diagnosticsChannel.channel('undici:client:connected').subscribe(({ connectParams, socket, connector }) => {\n    _socket = socket\n\n    assert.equal(Object.keys(connectParams).length, 7)\n    assert.equal(_connector, connector)\n\n    const { host, hostname, protocol, port, servername } = connectParams\n\n    assert.equal(host, `localhost:${server.address().port}`)\n    assert.equal(hostname, 'localhost')\n    assert.equal(port, String(server.address().port))\n    assert.equal(protocol, 'http:')\n    assert.equal(servername, null)\n  })\n\n  diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(({ request, headers, socket }) => {\n    assert.equal(_req, request)\n    assert.equal(_socket, socket)\n\n    const expectedHeaders = [\n      'POST / HTTP/1.1',\n      `host: localhost:${server.address().port}`,\n      'connection: keep-alive',\n      'bar: bar',\n      'hello: world'\n    ]\n\n    assert.deepStrictEqual(headers, expectedHeaders.join('\\r\\n') + '\\r\\n')\n  })\n\n  diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => {\n    assert.equal(_req, request)\n    assert.equal(response.statusCode, 200)\n    const expectedHeaders = [\n      Buffer.from('Content-Type'),\n      Buffer.from('text/plain'),\n      Buffer.from('trailer'),\n      Buffer.from('foo'),\n      Buffer.from('Date'),\n      response.headers[5], // This is a date\n      Buffer.from('Connection'),\n      Buffer.from('keep-alive'),\n      Buffer.from('Keep-Alive'),\n      Buffer.from('timeout=5'),\n      Buffer.from('Transfer-Encoding'),\n      Buffer.from('chunked')\n    ]\n    assert.deepStrictEqual(response.headers, expectedHeaders)\n    assert.equal(response.statusText, 'OK')\n  })\n\n  let bodySent = false\n  const bodyChunks = []\n  diagnosticsChannel.channel('undici:request:bodyChunkSent').subscribe(({ request, chunk }) => {\n    assert.equal(_req, request)\n    assert.equal(Buffer.isBuffer(chunk), true)\n    bodyChunks.push(chunk)\n  })\n  diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {\n    assert.equal(_req, request)\n    bodySent = true\n\n    const requestBody = Buffer.concat(bodyChunks)\n    assert.deepStrictEqual(requestBody, Buffer.from('hello world'))\n  })\n\n  let endEmitted = false\n\n  return new Promise((resolve) => {\n    const respChunks = []\n    diagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ request, chunk }) => {\n      assert.equal(_req, request)\n      respChunks.push(chunk)\n    })\n\n    diagnosticsChannel.channel('undici:request:trailers').subscribe(({ request, trailers }) => {\n      assert.equal(bodySent, true)\n      assert.equal(request.completed, true)\n      assert.equal(_req, request)\n      // This event is emitted after the last chunk has been added to the body stream,\n      // not when it was consumed by the application\n      assert.equal(endEmitted, false)\n      assert.deepStrictEqual(trailers, [Buffer.from('foo'), Buffer.from('oof')])\n\n      const respData = Buffer.concat(respChunks)\n      assert.deepStrictEqual(respData, Buffer.from('hello'))\n\n      resolve()\n    })\n\n    server.listen(0, () => {\n      const client = new Client(`http://localhost:${server.address().port}`, {\n        keepAliveTimeout: 300e3\n      })\n\n      client.request({\n        path: '/',\n        method: 'POST',\n        headers: reqHeaders,\n        body: 'hello world'\n      }, (err, data) => {\n        assert.ok(!err)\n        client.close()\n\n        data.body.on('end', function () {\n          endEmitted = true\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/node-test/large-body.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { request } = require('../../')\nconst { strictEqual } = require('node:assert')\n\ntest('socket should not be reused unless body is consumed', async (t) => {\n  const LARGE_BODY = 'x'.repeat(10000000)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (req.url === '/foo') {\n      res.end(LARGE_BODY)\n      return\n    }\n    if (req.url === '/bar') {\n      res.end('bar')\n      return\n    }\n    throw new Error('Unexpected request url: ' + req.url)\n  })\n\n  await new Promise((resolve) => { server.listen(0, resolve) })\n  t.after(() => { server.close() })\n\n  // Works fine\n  // const fooRes = await request('http://localhost:3000/foo')\n  // const fooBody = await fooRes.body.text()\n\n  // const barRes = await request('http://localhost:3000/bar')\n  // await barRes.body.text()\n\n  const port = server.address().port\n\n  // Fails with:\n  const fooRes = await request(`http://localhost:${port}/foo`)\n  const barRes = await request(`http://localhost:${port}/bar`)\n\n  const fooBody = await fooRes.body.text()\n  await barRes.body.text()\n\n  strictEqual(fooRes.headers['content-length'], String(LARGE_BODY.length))\n  strictEqual(fooBody.length, LARGE_BODY.length)\n  strictEqual(fooBody, LARGE_BODY)\n})\n"
  },
  {
    "path": "test/node-test/tree.js",
    "content": "'use strict'\n\nconst { TernarySearchTree, tree } = require('../../lib/core/tree')\nconst { wellknownHeaderNames, headerNameLowerCasedRecord } = require('../../lib/core/constants')\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\n\ndescribe('Ternary Search Tree', () => {\n  test('The empty key cannot be added.', () => {\n    assert.throws(() => new TernarySearchTree().insert('', ''))\n    const tst = new TernarySearchTree()\n    tst.insert('a', 'a')\n    assert.throws(() => tst.insert('', ''))\n  })\n\n  test('looking up not inserted key returns null', () => {\n    const tst = new TernarySearchTree()\n    tst.insert('a', 'a')\n    assert.strictEqual(tst.lookup(Buffer.from('non-existent')), null)\n  })\n\n  test('not ascii string', () => {\n    assert.throws(() => new TernarySearchTree().insert('\\x80', 'a'))\n    const tst = new TernarySearchTree()\n    tst.insert('a', 'a')\n    // throw on TstNode\n    assert.throws(() => tst.insert('\\x80', 'a'))\n  })\n\n  test('duplicate key', () => {\n    const tst = new TernarySearchTree()\n    const key = 'a'\n    const lookupKey = Buffer.from(key)\n    tst.insert(key, 'a')\n    assert.strictEqual(tst.lookup(lookupKey), 'a')\n    tst.insert(key, 'b')\n    assert.strictEqual(tst.lookup(lookupKey), 'b')\n  })\n\n  test('tree', () => {\n    for (let i = 0; i < wellknownHeaderNames.length; ++i) {\n      const key = wellknownHeaderNames[i]\n      assert.strictEqual(tree.lookup(Buffer.from(key)), headerNameLowerCasedRecord[key])\n    }\n  })\n\n  test('fuzz', () => {\n    const LENGTH = 2000\n    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\n    const charactersLength = characters.length\n\n    function generateAsciiString (length) {\n      let result = ''\n      for (let i = 0; i < length; ++i) {\n        result += characters[Math.floor(Math.random() * charactersLength)]\n      }\n      return result\n    }\n    const tst = new TernarySearchTree()\n\n    /** @type {string[]} */\n    const random = new Array(LENGTH)\n    /** @type {Buffer[]} */\n    const randomBuffer = new Array(LENGTH)\n\n    for (let i = 0; i < LENGTH; ++i) {\n      const key = generateAsciiString((Math.random() * 100 + 5) | 0)\n      const lowerCasedKey = random[i] = key.toLowerCase()\n      randomBuffer[i] = Buffer.from(key)\n      tst.insert(lowerCasedKey, lowerCasedKey)\n    }\n\n    for (let i = 0; i < LENGTH; ++i) {\n      assert.strictEqual(tst.lookup(randomBuffer[i]), random[i])\n    }\n  })\n})\n"
  },
  {
    "path": "test/node-test/unix.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Client, Pool } = require('../../')\nconst http = require('node:http')\nconst https = require('node:https')\nconst pem = require('@metcoder95/https-pem')\nconst fs = require('node:fs')\nconst { tspl } = require('@matteo.collina/tspl')\n\nconst skip = process.platform === 'win32'\n\ntest('http unix get', { skip }, async (t) => {\n  let client\n  const p = tspl(t, { plan: 7 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.equal('/', req.url)\n    p.equal('GET', req.method)\n    p.equal('localhost', req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n\n  t.after(() => {\n    server.close()\n    client.close()\n  })\n\n  try {\n    fs.unlinkSync('/var/tmp/test3.sock')\n  } catch (err) {\n\n  }\n\n  server.listen('/var/tmp/test3.sock', () => {\n    client = new Client({\n      hostname: 'localhost',\n      protocol: 'http:'\n    }, {\n      socketPath: '/var/tmp/test3.sock'\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ifError(err)\n      const { statusCode, headers, body } = data\n      p.equal(statusCode, 200)\n      p.equal(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        p.equal('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('http unix get pool', { skip }, async (t) => {\n  let client\n  const p = tspl(t, { plan: 7 })\n\n  const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    p.equal('/', req.url)\n    p.equal('GET', req.method)\n    p.equal('localhost', req.headers.host)\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n\n  t.after(() => {\n    server.close()\n    client.close()\n  })\n\n  try {\n    fs.unlinkSync('/var/tmp/test3.sock')\n  } catch (err) {\n\n  }\n\n  server.listen('/var/tmp/test3.sock', () => {\n    client = new Pool({\n      hostname: 'localhost',\n      protocol: 'http:'\n    }, {\n      socketPath: '/var/tmp/test3.sock'\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ifError(err)\n      const { statusCode, headers, body } = data\n      p.equal(statusCode, 200)\n      p.equal(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        p.equal('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await p.completed\n})\n\ntest('https get with tls opts', { skip }, async (t) => {\n  let client\n  const p = tspl(t, { plan: 6 })\n\n  const server = https.createServer({ ...pem, joinDuplicateHeaders: true }, (req, res) => {\n    p.equal('/', req.url)\n    p.equal('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n\n  t.after(() => {\n    server.close()\n    client.close()\n  })\n\n  try {\n    fs.unlinkSync('/var/tmp/test3.sock')\n  } catch (err) {\n\n  }\n\n  server.listen('/var/tmp/test3.sock', () => {\n    client = new Client({\n      hostname: 'localhost',\n      protocol: 'https:'\n    }, {\n      socketPath: '/var/tmp/test3.sock',\n      tls: {\n        rejectUnauthorized: false\n      }\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, data) => {\n      p.ifError(err)\n      const { statusCode, headers, body } = data\n      p.equal(statusCode, 200)\n      p.equal(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        p.equal('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n  await p.completed\n})\n"
  },
  {
    "path": "test/node-test/util.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { Stream } = require('node:stream')\nconst { EventEmitter } = require('node:events')\n\nconst util = require('../../lib/core/util')\nconst { headerNameLowerCasedRecord } = require('../../lib/core/constants')\nconst { InvalidArgumentError } = require('../../lib/core/errors')\n\ntest('isStream', () => {\n  const stream = new Stream()\n  assert.ok(util.isStream(stream))\n\n  const buffer = Buffer.alloc(0)\n  assert.ok(util.isStream(buffer) === false)\n\n  const ee = new EventEmitter()\n  assert.ok(util.isStream(ee) === false)\n})\n\ntest('getServerName', () => {\n  assert.equal(util.getServerName('1.1.1.1'), '')\n  assert.equal(util.getServerName('1.1.1.1:443'), '')\n  assert.equal(util.getServerName('example.com'), 'example.com')\n  assert.equal(util.getServerName('example.com:80'), 'example.com')\n  assert.equal(util.getServerName('[2606:4700:4700::1111]'), '')\n  assert.equal(util.getServerName('[2606:4700:4700::1111]:443'), '')\n})\n\ntest('assertRequestHandler', () => {\n  assert.throws(() => util.assertRequestHandler(null), InvalidArgumentError, 'handler must be an object')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: null\n  }), InvalidArgumentError, 'invalid onConnect method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: null\n  }), InvalidArgumentError, 'invalid onError method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: null\n  }), InvalidArgumentError, 'invalid onBodySent method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: () => {},\n    onHeaders: null\n  }), InvalidArgumentError, 'invalid onHeaders method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: () => {},\n    onHeaders: () => {},\n    onData: null\n  }), InvalidArgumentError, 'invalid onData method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: () => {},\n    onHeaders: () => {},\n    onData: () => {},\n    onComplete: null\n  }), InvalidArgumentError, 'invalid onComplete method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: () => {},\n    onUpgrade: 'null'\n  }, 'CONNECT'), InvalidArgumentError, 'invalid onUpgrade method')\n  assert.throws(() => util.assertRequestHandler({\n    onConnect: () => {},\n    onError: () => {},\n    onBodySent: () => {},\n    onUpgrade: 'null'\n  }, 'CONNECT', () => {}), InvalidArgumentError, 'invalid onUpgrade method')\n})\n\ntest('parseHeaders', () => {\n  assert.deepEqual(util.parseHeaders(['key', 'value']), { key: 'value' })\n  assert.deepEqual(util.parseHeaders([Buffer.from('key'), Buffer.from('value')]), { key: 'value' })\n  assert.deepEqual(util.parseHeaders(['Key', 'Value']), { key: 'Value' })\n  assert.deepEqual(util.parseHeaders(['Key', 'value', 'key', 'Value']), { key: ['value', 'Value'] })\n  assert.deepEqual(util.parseHeaders(['key', ['value1', 'value2', 'value3']]), { key: ['value1', 'value2', 'value3'] })\n  assert.deepEqual(util.parseHeaders([Buffer.from('key'), [Buffer.from('value1'), Buffer.from('value2'), Buffer.from('value3')]]), { key: ['value1', 'value2', 'value3'] })\n})\n\ntest('parseHeaders decodes values as latin1, not utf8', () => {\n  // These bytes (0xE2, 0x80, 0xA6) are the UTF-8 encoding of U+2026 (ellipsis)\n  // When decoded as latin1, they should be 3 separate characters: â, €, ¦\n  // When incorrectly decoded as UTF-8, they would be a single character: …\n  const latin1Bytes = Buffer.from([0xe2, 0x80, 0xa6])\n  const result = util.parseHeaders([Buffer.from('x-test'), latin1Bytes])\n\n  assert.strictEqual(result['x-test'].length, 3)\n  assert.strictEqual(result['x-test'].charCodeAt(0), 0xe2)\n  assert.strictEqual(result['x-test'].charCodeAt(1), 0x80)\n  assert.strictEqual(result['x-test'].charCodeAt(2), 0xa6)\n})\n\ntest('parseHeaders decodes duplicate header values as latin1', () => {\n  const latin1Bytes = Buffer.from([0xe2, 0x80, 0xa6])\n  const result = util.parseHeaders([\n    Buffer.from('x-test'), Buffer.from('first'),\n    Buffer.from('x-test'), latin1Bytes\n  ])\n\n  assert.deepEqual(result['x-test'][0], 'first')\n  assert.strictEqual(result['x-test'][1].length, 3)\n  assert.strictEqual(result['x-test'][1].charCodeAt(0), 0xe2)\n})\n\ntest('parseHeaders decodes array header values as latin1', () => {\n  const latin1Bytes = Buffer.from([0xe2, 0x80, 0xa6])\n  const result = util.parseHeaders([Buffer.from('x-test'), [latin1Bytes, latin1Bytes]])\n\n  assert.strictEqual(result['x-test'].length, 2)\n  assert.strictEqual(result['x-test'][0].length, 3)\n  assert.strictEqual(result['x-test'][0].charCodeAt(0), 0xe2)\n})\n\ntest('parseRawHeaders', () => {\n  assert.deepEqual(util.parseRawHeaders(['key', 'value', Buffer.from('key'), Buffer.from('value')]), ['key', 'value', 'key', 'value'])\n  assert.deepEqual(util.parseRawHeaders(['content-length', 'value', 'content-disposition', 'form-data; name=\"fieldName\"']), ['content-length', 'value', 'content-disposition', 'form-data; name=\"fieldName\"'])\n})\n\ntest('parseRawHeaders decodes values as latin1, not utf8', () => {\n  // These bytes (0xE2, 0x80, 0xA6) are the UTF-8 encoding of U+2026 (ellipsis)\n  // When decoded as latin1, they should be 3 separate characters\n  // When incorrectly decoded as UTF-8, they would be a single character\n  const latin1Bytes = Buffer.from([0xe2, 0x80, 0xa6])\n  const result = util.parseRawHeaders([Buffer.from('x-test'), latin1Bytes])\n\n  assert.strictEqual(result[0], 'x-test')\n  assert.strictEqual(result[1].length, 3)\n  assert.strictEqual(result[1].charCodeAt(0), 0xe2)\n  assert.strictEqual(result[1].charCodeAt(1), 0x80)\n  assert.strictEqual(result[1].charCodeAt(2), 0xa6)\n})\n\ntest('serializePathWithQuery', () => {\n  const tests = [\n    [{ id: BigInt(123456) }, 'id=123456'],\n    [{ date: new Date() }, 'date='],\n    [{ obj: { id: 1 } }, 'obj='],\n    [{ params: ['a', 'b', 'c'] }, 'params=a&params=b&params=c'],\n    [{ bool: true }, 'bool=true'],\n    [{ number: 123456 }, 'number=123456'],\n    [{ string: 'hello' }, 'string=hello'],\n    [{ null: null }, 'null='],\n    [{ void: undefined }, 'void='],\n    [{ fn: function () {} }, 'fn='],\n    [{}, '']\n  ]\n\n  const base = 'https://www.google.com'\n\n  for (const [input, output] of tests) {\n    const expected = `${base}${output ? `?${output}` : output}`\n    assert.deepEqual(util.serializePathWithQuery(base, input), expected)\n  }\n})\n\ntest('headerNameLowerCasedRecord', () => {\n  assert.ok(typeof headerNameLowerCasedRecord.hasOwnProperty !== 'function')\n})\n"
  },
  {
    "path": "test/node-test/validations.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { createServer } = require('node:http')\nconst { Client } = require('../../')\nconst { tspl } = require('@matteo.collina/tspl')\n\ntest('validations', async t => {\n  let client\n  const p = tspl(t, { plan: 10 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n    p.fail('server should never be called')\n  })\n\n  t.after(() => {\n    server.close()\n    client.close()\n  })\n\n  server.listen(0, async () => {\n    const url = `http://localhost:${server.address().port}`\n    client = new Client(url)\n\n    await t.test('path', () => {\n      client.request({ path: null, method: 'GET' }, (err, res) => {\n        p.equal(err.code, 'UND_ERR_INVALID_ARG')\n        p.equal(err.message, 'path must be a string')\n      })\n\n      client.request({ path: 'aaa', method: 'GET' }, (err, res) => {\n        p.equal(err.code, 'UND_ERR_INVALID_ARG')\n        p.equal(err.message, 'path must be an absolute URL or start with a slash')\n      })\n    })\n\n    await t.test('method', () => {\n      client.request({ path: '/', method: null }, (err, res) => {\n        p.equal(err.code, 'UND_ERR_INVALID_ARG')\n        p.equal(err.message, 'method must be a string')\n      })\n    })\n\n    await t.test('body', () => {\n      client.request({ path: '/', method: 'POST', body: 42 }, (err, res) => {\n        p.equal(err.code, 'UND_ERR_INVALID_ARG')\n        p.equal(err.message, 'body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')\n      })\n\n      client.request({ path: '/', method: 'POST', body: { hello: 'world' } }, (err, res) => {\n        p.equal(err.code, 'UND_ERR_INVALID_ARG')\n        p.equal(err.message, 'body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable')\n      })\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/parser-issues.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst net = require('node:net')\nconst { Client, errors } = require('..')\n\ntest('https://github.com/mcollina/undici/issues/268', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\n')\n    socket.write('Transfer-Encoding: chunked\\r\\n\\r\\n')\n    setTimeout(() => {\n      socket.write('1\\r\\n')\n      socket.write('\\n\\r\\n')\n      setTimeout(() => {\n        socket.write('1\\r\\n')\n        socket.write('\\n\\r\\n')\n      }, 500)\n    }, 500)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client.request({\n      method: 'GET',\n      path: '/nxt/_changes?feed=continuous&heartbeat=5000',\n      headersTimeout: 1e3\n    }, (err, data) => {\n      t.ifError(err)\n      data.body\n        .resume()\n      setTimeout(() => {\n        t.ok(true, 'pass')\n        data.body.on('error', () => {})\n      }, 2e3)\n    })\n  })\n\n  await t.completed\n})\n\ntest('parser fail', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTT/1.1 200 OK\\r\\n')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      method: 'GET',\n      path: '/'\n    }, (err, data) => {\n      t.ok(err)\n      t.ok(err instanceof errors.HTTPParserError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('split header field', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\nA')\n    setTimeout(() => {\n      socket.write('SD: asd,asd\\r\\n\\r\\n\\r\\n')\n    }, 100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      method: 'GET',\n      path: '/'\n    }, (err, data) => {\n      t.ifError(err)\n      t.equal(data.headers.asd, 'asd,asd')\n      data.body.destroy().on('error', () => {})\n    })\n  })\n\n  await t.completed\n})\n\ntest('split header value', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer(socket => {\n    socket.write('HTTP/1.1 200 OK\\r\\nASD: asd')\n    setTimeout(() => {\n      socket.write(',asd\\r\\n\\r\\n\\r\\n')\n    }, 100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.request({\n      method: 'GET',\n      path: '/'\n    }, (err, data) => {\n      t.ifError(err)\n      t.equal(data.headers.asd, 'asd,asd')\n      data.body.destroy().on('error', () => {})\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/pipeline-pipelining.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { kConnect } = require('../lib/core/symbols')\nconst { kBusy, kPending, kRunning } = require('../lib/core/symbols')\n\ntest('pipeline pipelining', async (t) => {\n  t = tspl(t, { plan: 10 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.deepStrictEqual(req.headers['transfer-encoding'], undefined)\n    res.end()\n  })\n\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 2\n    })\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    client[kConnect](() => {\n      t.equal(client[kRunning], 0)\n      client.pipeline({\n        method: 'GET',\n        path: '/',\n        blocking: false\n      }, ({ body }) => body).end().resume()\n      t.equal(client[kBusy], true)\n      t.deepStrictEqual(client[kRunning], 0)\n      t.deepStrictEqual(client[kPending], 1)\n\n      client.pipeline({\n        method: 'GET',\n        path: '/',\n        blocking: false\n      }, ({ body }) => body).end().resume()\n      t.equal(client[kBusy], true)\n      t.deepStrictEqual(client[kRunning], 0)\n      t.deepStrictEqual(client[kPending], 2)\n      process.nextTick(() => {\n        t.equal(client[kRunning], 2)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipeline pipelining retry', async (t) => {\n  t = tspl(t, { plan: 13 })\n\n  let count = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (count++ === 0) {\n      res.destroy()\n    } else {\n      res.end()\n    }\n  })\n\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 3\n    })\n    after(() => client.destroy())\n\n    client.once('disconnect', () => {\n      t.ok(true, 'pass')\n    })\n\n    client[kConnect](() => {\n      client.pipeline({\n        method: 'GET',\n        path: '/',\n        blocking: false\n      }, ({ body }) => body).end().resume()\n        .on('error', (err) => {\n          t.ok(err)\n        })\n      t.equal(client[kBusy], true)\n      t.deepStrictEqual(client[kRunning], 0)\n      t.deepStrictEqual(client[kPending], 1)\n\n      client.pipeline({\n        method: 'GET',\n        path: '/',\n        blocking: false\n      }, ({ body }) => body).end().resume()\n      t.equal(client[kBusy], true)\n      t.deepStrictEqual(client[kRunning], 0)\n      t.deepStrictEqual(client[kPending], 2)\n\n      client.pipeline({\n        method: 'GET',\n        path: '/',\n        blocking: false\n      }, ({ body }) => body).end().resume()\n      t.equal(client[kBusy], true)\n      t.deepStrictEqual(client[kRunning], 0)\n      t.deepStrictEqual(client[kPending], 3)\n\n      process.nextTick(() => {\n        t.equal(client[kRunning], 3)\n      })\n\n      client.close(() => {\n        t.ok(true, 'pass')\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/pool-connection-error-memory-leak.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { Pool } = require('..')\nconst { createServer } = require('node:http')\nconst { kClients } = require('../lib/dispatcher/pool-base')\n\n// This test verifies that clients are properly removed from the pool when they encounter connection errors,\n// which is the fix implemented for issue #3895 (memory leak with connection errors)\ntest('Pool client count does not grow on repeated connection errors', async (t) => {\n  // Setup a pool pointing to a non-existent server\n  const pool = new Pool('http://localhost:1', {\n    connections: 10,\n    connectTimeout: 100, // Short timeout to speed up the test\n    bodyTimeout: 100,\n    headersTimeout: 100\n  })\n\n  try {\n    const clientCounts = []\n\n    // Track initial client count\n    clientCounts.push(pool[kClients].length)\n\n    // Make several requests that will fail with connection errors\n    const requests = 5\n    for (let i = 0; i < requests; i++) {\n      try {\n        await pool.request({\n          path: `/${i}`,\n          method: 'GET'\n        })\n        assert.fail('Request should have failed with a connection error')\n      } catch (err) {\n        // We expect connection errors, but the error might be wrapped\n        assert.ok(\n          err.code === 'ECONNREFUSED' ||\n          err.cause?.code === 'ECONNREFUSED' ||\n          err.code === 'UND_ERR_CONNECT',\n          `Expected connection error but got: ${err.message} (${err.code})`\n        )\n      }\n\n      // Track client count after each request\n      clientCounts.push(pool[kClients].length)\n\n      // Small delay to allow for client cleanup\n      await new Promise(resolve => setTimeout(resolve, 10))\n    }\n\n    // The key test: verify that client count doesn't increase monotonically,\n    // which would indicate the memory leak that was fixed\n    const maxCount = Math.max(...clientCounts)\n    assert.ok(\n      clientCounts[clientCounts.length - 1] <= maxCount,\n      `Client count should not increase continuously. Counts: ${clientCounts.join(', ')}`\n    )\n\n    // Ensure the last two counts are similar (stabilized)\n    const lastCount = clientCounts[clientCounts.length - 1]\n    const secondLastCount = clientCounts[clientCounts.length - 2]\n\n    assert.ok(\n      Math.abs(lastCount - secondLastCount) <= 1,\n      `Client count should stabilize. Last counts: ${secondLastCount}, ${lastCount}`\n    )\n\n    // Additional verification: make many more requests to check for significant growth\n    const moreRequests = 10\n    const startCount = pool[kClients].length\n\n    for (let i = 0; i < moreRequests; i++) {\n      try {\n        await pool.request({\n          path: `/more-${i}`,\n          method: 'GET'\n        })\n      } catch (err) {\n        // Expected error\n      }\n\n      // Small delay to allow for client cleanup\n      await new Promise(resolve => setTimeout(resolve, 10))\n    }\n\n    const endCount = pool[kClients].length\n\n    // The maximum tolerable growth - some growth may occur due to timing issues,\n    // but it should be limited and not proportional to the number of requests\n    const maxGrowth = 3\n\n    assert.ok(\n      endCount - startCount <= maxGrowth,\n      `Client count should not grow significantly after many failed requests. Start: ${startCount}, End: ${endCount}`\n    )\n  } finally {\n    await pool.close()\n  }\n})\n\n// This test specifically verifies the fix in pool-base.js for connectionError event\ntest('Pool clients are removed on connectionError event', async (t) => {\n  // Create a server we'll use to track connection events\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, { 'Content-Type': 'text/plain' })\n    res.end('ok')\n  })\n\n  await new Promise(resolve => server.listen(0, resolve))\n  const port = server.address().port\n\n  const pool = new Pool(`http://localhost:${port}`, {\n    connections: 3 // Small pool to make testing easier\n  })\n\n  try {\n    // Make an initial successful request to create a client\n    await pool.request({\n      path: '/',\n      method: 'GET'\n    })\n\n    // Save the initial number of clients\n    const initialCount = pool[kClients].length\n    assert.ok(initialCount > 0, 'Should have at least one client after a successful request')\n\n    // Manually trigger a connectionError on all clients\n    for (const client of [...pool[kClients]]) {\n      client.emit('connectionError', 'origin', [client], new Error('Simulated connection error'))\n    }\n\n    // Allow some time for the event to be processed\n    await new Promise(resolve => setTimeout(resolve, 50))\n\n    // After the fix, all clients should be removed when they emit a connectionError\n    assert.strictEqual(\n      pool[kClients].length,\n      0,\n      'All clients should be removed from pool after connectionError events'\n    )\n\n    // Make another request to verify the pool can create new clients\n    await pool.request({\n      path: '/after-error',\n      method: 'GET'\n    })\n\n    // Verify new clients were created\n    assert.ok(\n      pool[kClients].length > 0,\n      'Pool should create new clients after previous ones were removed'\n    )\n  } finally {\n    await pool.close()\n    await new Promise(resolve => server.close(resolve))\n  }\n})\n"
  },
  {
    "path": "test/pool.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { EventEmitter } = require('node:events')\nconst { createServer } = require('node:http')\nconst net = require('node:net')\nconst {\n  finished,\n  PassThrough,\n  Readable\n} = require('node:stream')\nconst { promisify } = require('node:util')\nconst {\n  kBusy,\n  kPending,\n  kRunning,\n  kSize,\n  kUrl\n} = require('../lib/core/symbols')\nconst {\n  Client,\n  Pool,\n  errors\n} = require('..')\n\ntest('throws when connection is infinite', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new Pool(null, { connections: 0 / 0 }) // eslint-disable-line\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid connections')\n  }\n})\n\ntest('throws when connections is negative', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new Pool(null, { connections: -1 }) // eslint-disable-line no-new\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid connections')\n  }\n})\n\ntest('throws when connection is not number', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new Pool(null, { connections: true }) // eslint-disable-line no-new\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid connections')\n  }\n})\n\ntest('throws when factory is not a function', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new Pool(null, { factory: '' }) // eslint-disable-line no-new\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'factory must be a function.')\n  }\n})\n\ntest('does not throw when connect is a function', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  t.doesNotThrow(() => new Pool('http://localhost', { connect: () => {} }))\n})\n\ntest('passes socketPath to custom connect function', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const connectError = new Error('custom connect error')\n  const socketPath = '/var/run/test.sock'\n  const pool = new Pool('http://localhost', {\n    socketPath,\n    connect (opts, cb) {\n      t.strictEqual(opts.socketPath, socketPath)\n      cb(connectError, null)\n    }\n  })\n  after(() => pool.close())\n\n  pool.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    t.strictEqual(err, connectError)\n  })\n\n  await t.completed\n})\n\ntest('connect/disconnect event(s)', async (t) => {\n  const clients = 2\n\n  t = tspl(t, { plan: clients * 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      Connection: 'keep-alive',\n      'Keep-Alive': 'timeout=1s'\n    })\n    res.end('ok')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const pool = new Pool(`http://localhost:${server.address().port}`, {\n      connections: clients,\n      keepAliveTimeoutThreshold: 100\n    })\n    after(() => pool.close())\n\n    pool.on('connect', (origin, [pool, client]) => {\n      t.strictEqual(client instanceof Client, true)\n    })\n    pool.on('disconnect', (origin, [pool, client], error) => {\n      t.ok(client instanceof Client)\n      t.ok(error instanceof errors.InformationalError)\n      t.strictEqual(error.code, 'UND_ERR_INFO')\n      t.strictEqual(error.message, 'socket idle timeout')\n    })\n\n    for (let i = 0; i < clients; i++) {\n      pool.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { headers, body }) => {\n        t.ifError(err)\n        body.resume()\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('basic get', async (t) => {\n  t = tspl(t, { plan: 14 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    t.strictEqual(client[kUrl].origin, `http://localhost:${server.address().port}`)\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    t.strictEqual(client.destroyed, false)\n    t.strictEqual(client.closed, false)\n    client.close((err) => {\n      t.ifError(err)\n      t.strictEqual(client.destroyed, true)\n      client.destroy((err) => {\n        t.ifError(err)\n        client.close((err) => {\n          t.ok(err instanceof errors.ClientDestroyedError)\n        })\n      })\n    })\n    t.strictEqual(client.closed, true)\n  })\n\n  await t.completed\n})\n\ntest('URL as arg', async (t) => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const url = new URL('http://localhost')\n    url.port = server.address().port\n    const client = new Pool(url)\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    client.close((err) => {\n      t.ifError(err)\n      client.destroy((err) => {\n        t.ifError(err)\n        client.close((err) => {\n          t.ok(err instanceof errors.ClientDestroyedError)\n        })\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic get error async/await', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    await client.request({ path: '/', method: 'GET' })\n      .catch((err) => {\n        t.ok(err)\n      })\n\n    await client.destroy()\n\n    await client.close().catch((err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('basic get with async/await', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  await promisify(server.listen.bind(server))(0)\n  const client = new Pool(`http://localhost:${server.address().port}`)\n  after(() => client.destroy())\n\n  const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n  t.strictEqual(statusCode, 200)\n  t.strictEqual(headers['content-type'], 'text/plain')\n\n  body.resume()\n  await promisify(finished)(body)\n\n  await client.close()\n  await client.destroy()\n})\n\ntest('stream get async/await', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  await promisify(server.listen.bind(server))(0)\n  const client = new Pool(`http://localhost:${server.address().port}`)\n  after(() => client.destroy())\n\n  await client.stream({ path: '/', method: 'GET' }, ({ statusCode, headers }) => {\n    t.strictEqual(statusCode, 200)\n    t.strictEqual(headers['content-type'], 'text/plain')\n    return new PassThrough()\n  })\n\n  await t.completed\n})\n\ntest('stream get error async/await', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    await client.stream({ path: '/', method: 'GET' }, () => {\n\n    })\n      .catch((err) => {\n        t.ok(err)\n      })\n  })\n\n  await t.completed\n})\n\ntest('pipeline get', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const bufs = []\n    client.pipeline({ path: '/', method: 'GET' }, ({ statusCode, headers, body }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      return body\n    })\n      .end()\n      .on('data', (buf) => {\n        bufs.push(buf)\n      })\n      .on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n  })\n\n  await t.completed\n})\n\ntest('backpressure algorithm', async (t) => {\n  t = tspl(t, { plan: 12 })\n\n  const seen = []\n  let total = 0\n\n  let writeMore = true\n\n  class FakeClient extends EventEmitter {\n    constructor () {\n      super()\n\n      this.id = total++\n    }\n\n    dispatch (req, handler) {\n      seen.push({ req, client: this, id: this.id })\n      return writeMore\n    }\n  }\n\n  const noopHandler = {\n    onError (err) {\n      throw err\n    }\n  }\n\n  const pool = new Pool('http://notahost', {\n    factory: () => new FakeClient()\n  })\n\n  pool.dispatch({}, noopHandler)\n  pool.dispatch({}, noopHandler)\n\n  const d1 = seen.shift() // d1 = c0\n  t.strictEqual(d1.id, 0)\n  const d2 = seen.shift() // d2 = c0\n  t.strictEqual(d2.id, 0)\n\n  t.strictEqual(d1.id, d2.id)\n\n  writeMore = false\n\n  pool.dispatch({}, noopHandler) // d3 = c0\n\n  pool.dispatch({}, noopHandler) // d4 = c1\n\n  const d3 = seen.shift()\n  t.strictEqual(d3.id, 0)\n  const d4 = seen.shift()\n  t.strictEqual(d4.id, 1)\n\n  t.strictEqual(d3.id, d2.id)\n  t.notEqual(d3.id, d4.id)\n\n  writeMore = true\n\n  d4.client.emit('drain', new URL('http://notahost'), [d4.client])\n\n  pool.dispatch({}, noopHandler) // d5 = c1\n\n  d3.client.emit('drain', new URL('http://notahost'), [d3.client])\n\n  pool.dispatch({}, noopHandler) // d6 = c0\n\n  const d5 = seen.shift()\n  t.strictEqual(d5.id, 1)\n  const d6 = seen.shift()\n  t.strictEqual(d6.id, 0)\n\n  t.strictEqual(d5.id, d4.id)\n  t.strictEqual(d3.id, d6.id)\n\n  t.strictEqual(total, 3)\n\n  t.end()\n})\n\ntest('busy', async (t) => {\n  t = tspl(t, { plan: 8 * 16 + 2 + 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  const connections = 2\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections,\n      pipelining: 2\n    })\n    client.on('drain', () => {\n      t.ok(true, 'pass')\n    })\n    client.on('connect', () => {\n      t.ok(true, 'pass')\n    })\n    after(() => client.destroy())\n\n    for (let n = 1; n <= 8; ++n) {\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        t.ifError(err)\n        t.strictEqual(statusCode, 200)\n        t.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n      t.strictEqual(client[kPending], n)\n      t.strictEqual(client[kBusy], n > 1)\n      t.strictEqual(client[kSize], n)\n      t.strictEqual(client[kRunning], 0)\n\n      t.strictEqual(client.stats.connected, 0)\n      t.strictEqual(client.stats.free, 0)\n      t.strictEqual(client.stats.queued, Math.max(n - connections, 0))\n      t.strictEqual(client.stats.pending, n)\n      t.strictEqual(client.stats.size, n)\n      t.strictEqual(client.stats.running, 0)\n    }\n  })\n\n  await t.completed\n})\n\ntest('invalid pool dispatch options', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const pool = new Pool('http://notahost')\n  t.throws(() => pool.dispatch({}), errors.InvalidArgumentError, 'throws on invalid handler')\n  t.throws(() => pool.dispatch({}, {}), errors.InvalidArgumentError, 'throws on invalid handler')\n})\n\ntest('pool upgrade promise', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('upgrade: websocket\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n\n    c.on('end', () => {\n      c.end()\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const { headers, socket } = await client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'Websocket'\n    })\n\n    let recvData = ''\n    socket.on('data', (d) => {\n      recvData += d\n    })\n\n    socket.on('close', () => {\n      t.strictEqual(recvData.toString(), 'Body')\n    })\n\n    t.deepStrictEqual(headers, {\n      hello: 'world',\n      connection: 'upgrade',\n      upgrade: 'websocket'\n    })\n    socket.end()\n  })\n\n  await t.completed\n})\n\ntest('pool connect', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (c) => {\n    t.fail()\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const { socket } = await client.connect({\n      path: '/'\n    })\n\n    let recvData = ''\n    socket.on('data', (d) => {\n      recvData += d\n    })\n\n    socket.on('end', () => {\n      t.strictEqual(recvData.toString(), 'Body')\n    })\n\n    socket.write('Body')\n    socket.end()\n  })\n\n  await t.completed\n})\n\ntest('pool connect with clientTtl specified', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, t.fail)\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.write('HTTP/1.1 200 Connection established\\r\\n\\r\\n')\n\n    let data = firstBodyChunk.toString()\n    socket.on('data', (buf) => {\n      data += buf.toString()\n    })\n\n    socket.on('end', () => {\n      socket.end(data)\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      clientTtl: 10\n    })\n\n    const { socket } = await client.connect({\n      path: '/'\n    })\n\n    t.strictEqual(socket.closed, false, 'client not closed yet')\n\n    let recvData = ''\n    socket.on('data', (d) => {\n      recvData += d\n    })\n\n    socket.on('end', () => {\n      t.strictEqual(recvData.toString(), 'Body')\n    })\n\n    socket.write('Body')\n    await new Promise((resolve, reject) => socket.end((e) => e ? reject(e) : resolve()))\n\n    t.strictEqual(socket.closed, false, 'client not closed yet')\n\n    await new Promise(resolve => setTimeout(resolve, 10))\n    t.strictEqual(socket.closed, true, 'client closed after ttl')\n  })\n\n  await t.completed\n})\n\ntest('pool dispatch', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    let buf = ''\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n      },\n      onData (chunk) {\n        buf += chunk\n      },\n      onComplete () {\n        t.strictEqual(buf, 'asd')\n      },\n      onError () {\n      }\n    })\n  })\n\n  await t.completed\n})\n\ntest('pool pipeline args validation', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const client = new Pool('http://localhost:5000')\n\n  const ret = client.pipeline(null, () => {})\n  ret.on('error', (err) => {\n    t.ok(/opts/.test(err.message))\n    t.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  await t.completed\n})\n\ntest('300 requests succeed', async (t) => {\n  t = tspl(t, { plan: 300 * 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1\n    })\n    after(() => client.destroy())\n\n    for (let n = 0; n < 300; ++n) {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, data) => {\n        t.ifError(err)\n        data.body.on('data', (chunk) => {\n          t.strictEqual(chunk.toString(), 'asd')\n        }).on('end', () => {\n          t.ok(true, 'pass')\n        })\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('pool connect error', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (c) => {\n    t.fail()\n  })\n  server.on('connect', (req, socket, firstBodyChunk) => {\n    socket.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.connect({\n        path: '/'\n      })\n    } catch (err) {\n      t.ok(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('pool upgrade error', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', (d) => {\n      c.write('HTTP/1.1 101\\r\\n')\n      c.write('hello: world\\r\\n')\n      c.write('connection: upgrade\\r\\n')\n      c.write('\\r\\n')\n      c.write('Body')\n    })\n    c.on('error', () => {\n      // Whether we get an error, end or close is undefined.\n      // Ignore error.\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'Websocket'\n      })\n    } catch (err) {\n      t.ok(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('pool dispatch error', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n      },\n      onData (chunk) {\n      },\n      onComplete () {\n        t.ok(true, 'pass')\n      },\n      onError () {\n      }\n    })\n\n    client.dispatch({\n      path: '/',\n      method: 'GET',\n      headers: {\n        'transfer-encoding': 'fail'\n      }\n    }, {\n      onConnect () {\n        t.fail()\n      },\n      onHeaders (statusCode, headers) {\n        t.fail()\n      },\n      onData (chunk) {\n        t.fail()\n      },\n      onError (err) {\n        t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n      }\n    })\n  })\n\n  await t.completed\n})\n\ntest('pool request abort in queue', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n      },\n      onData (chunk) {\n      },\n      onComplete () {\n        t.ok(true, 'pass')\n      },\n      onError () {\n      }\n    })\n\n    const signal = new EventEmitter()\n    client.request({\n      path: '/',\n      method: 'GET',\n      signal\n    }, (err) => {\n      t.strictEqual(err.code, 'UND_ERR_ABORTED')\n    })\n    signal.emit('abort')\n  })\n\n  await t.completed\n})\n\ntest('pool stream abort in queue', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n      },\n      onData (chunk) {\n      },\n      onComplete () {\n        t.ok(true, 'pass')\n      },\n      onError () {\n      }\n    })\n\n    const signal = new EventEmitter()\n    client.stream({\n      path: '/',\n      method: 'GET',\n      signal\n    }, ({ body }) => body, (err) => {\n      t.strictEqual(err.code, 'UND_ERR_ABORTED')\n    })\n    signal.emit('abort')\n  })\n\n  await t.completed\n})\n\ntest('pool pipeline abort in queue', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    client.dispatch({\n      path: '/',\n      method: 'GET'\n    }, {\n      onConnect () {\n      },\n      onHeaders (statusCode, headers) {\n        t.strictEqual(statusCode, 200)\n      },\n      onData (chunk) {\n      },\n      onComplete () {\n        t.ok(true, 'pass')\n      },\n      onError () {\n      }\n    })\n\n    const signal = new EventEmitter()\n    client.pipeline({\n      path: '/',\n      method: 'GET',\n      signal\n    }, ({ body }) => body).end().on('error', (err) => {\n      t.strictEqual(err.code, 'UND_ERR_ABORTED')\n    })\n    signal.emit('abort')\n  })\n\n  await t.completed\n})\n\ntest('pool stream constructor error destroy body', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    {\n      const body = new Readable({\n        read () {\n        }\n      })\n      client.stream({\n        path: '/',\n        method: 'GET',\n        body,\n        headers: {\n          'transfer-encoding': 'fail'\n        }\n      }, () => {\n        t.fail()\n      }, (err) => {\n        t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        t.strictEqual(body.destroyed, true)\n      })\n    }\n\n    {\n      const body = new Readable({\n        read () {\n        }\n      })\n      client.stream({\n        path: '/',\n        method: 'CONNECT',\n        body\n      }, () => {\n        t.fail()\n      }, (err) => {\n        t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        t.strictEqual(body.destroyed, true)\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('pool request constructor error destroy body', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.close())\n\n    {\n      const body = new Readable({\n        read () {\n        }\n      })\n      client.request({\n        path: '/',\n        method: 'GET',\n        body,\n        headers: {\n          'transfer-encoding': 'fail'\n        }\n      }, (err) => {\n        t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        t.strictEqual(body.destroyed, true)\n      })\n    }\n\n    {\n      const body = new Readable({\n        read () {\n        }\n      })\n      client.request({\n        path: '/',\n        method: 'CONNECT',\n        body\n      }, (err) => {\n        t.strictEqual(err.code, 'UND_ERR_INVALID_ARG')\n        t.strictEqual(body.destroyed, true)\n      })\n    }\n  })\n\n  await t.completed\n})\n\ntest('pool close waits for all requests', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.destroy())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.ifError(err)\n    })\n\n    client.close(() => {\n      t.ok(true, 'pass')\n    })\n\n    client.close(() => {\n      t.ok(true, 'pass')\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.ok(err instanceof errors.ClientClosedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('pool destroyed', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.destroy())\n\n    client.destroy()\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('pool destroy fails queued requests', async (t) => {\n  t = tspl(t, { plan: 6 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`, {\n      connections: 1,\n      pipelining: 1\n    })\n    after(() => client.destroy())\n\n    const _err = new Error()\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.strictEqual(err, _err)\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.strictEqual(err, _err)\n    })\n\n    t.strictEqual(client.destroyed, false)\n    client.destroy(_err, () => {\n      t.ok(true, 'pass')\n    })\n    t.strictEqual(client.destroyed, true)\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n  })\n  await t.completed\n})\n\ntest('stats', async (t) => {\n  t = tspl(t, { plan: 11 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    t.strictEqual(client[kUrl].origin, `http://localhost:${server.address().port}`)\n\n    client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(client.stats.connected, 1)\n      t.strictEqual(client.stats.free, 0)\n      t.strictEqual(client.stats.pending, 0)\n      t.strictEqual(client.stats.queued, 0)\n      t.strictEqual(client.stats.running, 1)\n      t.strictEqual(client.stats.size, 1)\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/promises.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, Pool } = require('..')\nconst { createServer } = require('node:http')\nconst { readFileSync, createReadStream } = require('node:fs')\nconst { wrapWithAsyncIterable } = require('./utils/async-iterators')\n\ntest('basic get, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n\nfunction postServer (t, expected) {\n  return function (req, res) {\n    t.strictEqual(req.url, '/')\n    t.strictEqual(req.method, 'POST')\n\n    req.setEncoding('utf8')\n    let data = ''\n\n    req.on('data', function (d) { data += d })\n\n    req.on('end', () => {\n      t.strictEqual(data, expected)\n      res.end('hello')\n    })\n  }\n}\n\ntest('basic POST with string, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, body } = await client.request({ path: '/', method: 'POST', body: expected })\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('basic POST with Buffer, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const expected = readFileSync(__filename)\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected.toString()))\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, body } = await client.request({ path: '/', method: 'POST', body: expected })\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('basic POST with stream, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, body } = await client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          'content-length': Buffer.byteLength(expected)\n        },\n        body: createReadStream(__filename)\n      })\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('basic POST with async-iterator, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const expected = readFileSync(__filename, 'utf8')\n\n  const server = createServer({ joinDuplicateHeaders: true }, postServer(t, expected))\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, body } = await client.request({\n        path: '/',\n        method: 'POST',\n        headers: {\n          'content-length': Buffer.byteLength(expected)\n        },\n        body: wrapWithAsyncIterable(createReadStream(__filename))\n      })\n      t.strictEqual(statusCode, 200)\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n\ntest('20 times GET with pipelining 10, async await support', async (t) => {\n  const num = 20\n  t = tspl(t, { plan: 2 * num + 1 })\n\n  const sleep = ms => new Promise((resolve, reject) => {\n    setTimeout(resolve, ms)\n  })\n\n  let count = 0\n  let countGreaterThanOne = false\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    count++\n    await sleep(10)\n    countGreaterThanOne = countGreaterThanOne || count > 1\n    res.end(req.url)\n  })\n  after(() => server.close())\n\n  // needed to check for a warning on the maxListeners on the socket\n  function onWarning (warning) {\n    if (!/ExperimentalWarning/.test(warning)) {\n      t.fail()\n    }\n  }\n  process.on('warning', onWarning)\n  after(() => {\n    process.removeListener('warning', onWarning)\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 10\n    })\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    for (let i = 0; i < num; i++) {\n      makeRequest(i)\n    }\n\n    async function makeRequest (i) {\n      await makeRequestAndExpectUrl(client, i, t)\n      count--\n      if (i === num - 1) {\n        t.ok(countGreaterThanOne, 'seen more than one parallel request')\n      }\n    }\n  })\n\n  await t.completed\n})\n\nasync function makeRequestAndExpectUrl (client, i, t) {\n  try {\n    const { statusCode, body } = await client.request({ path: '/' + i, method: 'GET', blocking: false })\n    t.strictEqual(statusCode, 200)\n    const bufs = []\n    body.on('data', (buf) => {\n      bufs.push(buf)\n    })\n    body.on('end', () => {\n      t.strictEqual('/' + i, Buffer.concat(bufs).toString('utf8'))\n    })\n  } catch (err) {\n    t.fail(err)\n  }\n  return true\n}\n\ntest('pool, async await support', async (t) => {\n  t = tspl(t, { plan: 5 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Pool(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    try {\n      const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    } catch (err) {\n      t.fail(err)\n    }\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/proxy-agent.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { request, fetch, setGlobalDispatcher, getGlobalDispatcher } = require('..')\nconst { InvalidArgumentError, SecureProxyConnectionError } = require('../lib/core/errors')\nconst ProxyAgent = require('../lib/dispatcher/proxy-agent')\nconst Pool = require('../lib/dispatcher/pool')\nconst { createServer } = require('node:http')\nconst https = require('node:https')\nconst { Socket } = require('node:net')\nconst { createProxy } = require('proxy')\n\nconst certs = (() => {\n  const forge = require('node-forge')\n  const createCert = (cn, issuer, keyLength = 2048) => {\n    const keys = forge.pki.rsa.generateKeyPair(keyLength)\n    const cert = forge.pki.createCertificate()\n    cert.publicKey = keys.publicKey\n    cert.serialNumber = '' + Date.now()\n    cert.validity.notBefore = new Date()\n    cert.validity.notAfter = new Date()\n    cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10)\n\n    const attrs = [{\n      name: 'commonName',\n      value: cn\n    }]\n    cert.setSubject(attrs)\n    const isCa = issuer === undefined\n    cert.setExtensions([{\n      name: 'basicConstraints',\n      cA: isCa\n    }, {\n      name: 'keyUsage',\n      keyCertSign: true,\n      digitalSignature: true,\n      nonRepudiation: true,\n      keyEncipherment: true,\n      dataEncipherment: true\n    }, {\n      name: 'extKeyUsage',\n      serverAuth: true,\n      clientAuth: true,\n      codeSigning: true,\n      emailProtection: true,\n      timeStamping: true\n    }, {\n      name: 'nsCertType',\n      client: true,\n      server: true,\n      email: true,\n      objsign: true,\n      sslCA: isCa,\n      emailCA: isCa,\n      objCA: isCa\n    }])\n\n    const alg = forge.md.sha256.create()\n    if (issuer !== undefined) {\n      cert.setIssuer(issuer.certificate.subject.attributes)\n      cert.sign(issuer.privateKey, alg)\n    } else {\n      cert.setIssuer(attrs)\n      cert.sign(keys.privateKey, alg)\n    }\n    return {\n      privateKey: keys.privateKey,\n      publicKey: keys.publicKey,\n      certificate: cert\n    }\n  }\n\n  const root = createCert('CA')\n  const server = createCert('agent1', root)\n  const client = createCert('client', root)\n  const proxy = createCert('proxy', root)\n\n  return {\n    root: {\n      key: forge.pki.privateKeyToPem(root.privateKey),\n      crt: forge.pki.certificateToPem(root.certificate)\n    },\n    server: {\n      key: forge.pki.privateKeyToPem(server.privateKey),\n      crt: forge.pki.certificateToPem(server.certificate)\n    },\n    client: {\n      key: forge.pki.privateKeyToPem(client.privateKey),\n      crt: forge.pki.certificateToPem(client.certificate)\n    },\n    proxy: {\n      key: forge.pki.privateKeyToPem(proxy.privateKey),\n      crt: forge.pki.certificateToPem(proxy.certificate)\n    }\n  }\n})()\n\ntest('should throw error when no uri is provided', (t) => {\n  t = tspl(t, { plan: 2 })\n  t.throws(() => new ProxyAgent(), InvalidArgumentError)\n  t.throws(() => new ProxyAgent({}), InvalidArgumentError)\n})\n\ntest('using auth in combination with token should throw', (t) => {\n  t = tspl(t, { plan: 1 })\n  t.throws(() => new ProxyAgent({\n    auth: 'foo',\n    token: 'Bearer bar',\n    uri: 'http://example.com'\n  }),\n  InvalidArgumentError\n  )\n})\n\ntest('should accept string, URL and object as options', (t) => {\n  t = tspl(t, { plan: 3 })\n  t.doesNotThrow(() => new ProxyAgent('http://example.com'))\n  t.doesNotThrow(() => new ProxyAgent(new URL('http://example.com')))\n  t.doesNotThrow(() => new ProxyAgent({ uri: 'http://example.com' }))\n})\n\ntest('use proxy-agent to connect through proxy (keep alive)', async (t) => {\n  t = tspl(t, { plan: 10 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n  delete proxy.authenticate\n\n  let _socket, _connectParams\n  diagnosticsChannel.channel('undici:proxy:connected').subscribe(({ socket, connectParams }) => {\n    _socket = socket\n    _connectParams = connectParams\n  })\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    proxyTunnel: true\n  })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.on('connect', (msg) => {\n    t.strictEqual(msg.headers['proxy-connection'], 'keep-alive')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n  t.ok(_socket instanceof Socket)\n  t.equal(_connectParams.origin, proxyUrl)\n  t.equal(_connectParams.path, serverUrl.replace('http://', ''))\n  t.equal(_connectParams.headers['proxy-connection'], 'keep-alive')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent to connect through proxy', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n  delete proxy.authenticate\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.on('connect', () => {\n    t.ok(true, 'should connect to proxy')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy agent to connect through proxy using Pool', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n  let resolveFirstConnect\n  let connectCount = 0\n\n  proxy.authenticate = async function (req) {\n    if (++connectCount === 2) {\n      t.ok(true, 'second connect should arrive while first is still inflight')\n      resolveFirstConnect()\n      return true\n    } else {\n      await new Promise((resolve) => {\n        resolveFirstConnect = resolve\n      })\n      return true\n    }\n  }\n\n  server.on('request', (req, res) => {\n    res.end()\n  })\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const clientFactory = (url, options) => {\n    return new Pool(url, options)\n  }\n  const proxyAgent = new ProxyAgent({ auth: Buffer.from('user:pass').toString('base64'), uri: proxyUrl, clientFactory })\n  const firstRequest = request(`${serverUrl}`, { dispatcher: proxyAgent })\n  const secondRequest = await request(`${serverUrl}`, { dispatcher: proxyAgent })\n  t.strictEqual((await firstRequest).statusCode, 200)\n  t.strictEqual(secondRequest.statusCode, 200)\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent to connect through proxy using path with params', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.on('connect', () => {\n    t.fail('proxy tunnel should not be established')\n  })\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent to connect through proxy using path with params with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.on('connect', () => {\n    t.ok(true, 'should call proxy')\n  })\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent to connect through proxy with basic auth in URL', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = new URL(`http://user:pass@localhost:${proxy.address().port}`)\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req, fn) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.fail('proxy tunnel should not be established')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent to connect through proxy with basic auth in URL with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 7 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = new URL(`http://user:pass@localhost:${proxy.address().port}`)\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req, fn) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.ok(true, 'proxy should be called')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with auth', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    auth: Buffer.from('user:pass').toString('base64'),\n    uri: proxyUrl,\n    proxyTunnel: false\n  })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.fail('proxy tunnel should not be established')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with auth with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 7 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    auth: Buffer.from('user:pass').toString('base64'),\n    uri: proxyUrl,\n    proxyTunnel: true\n  })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.ok(true, 'proxy should be called')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with token', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    token: `Bearer ${Buffer.from('user:pass').toString('base64')}`,\n    uri: proxyUrl,\n    proxyTunnel: false\n  })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Bearer ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.fail('proxy tunnel should not be established')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with token with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 7 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    token: `Bearer ${Buffer.from('user:pass').toString('base64')}`,\n    uri: proxyUrl,\n    proxyTunnel: true\n  })\n  const parsedOrigin = new URL(serverUrl)\n\n  proxy.authenticate = function (req) {\n    t.ok(true, 'authentication should be called')\n    return req.headers['proxy-authorization'] === `Bearer ${Buffer.from('user:pass').toString('base64')}`\n  }\n  proxy.on('connect', () => {\n    t.ok(true, 'proxy should be called')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with custom headers', async (t) => {\n  t = tspl(t, { plan: 1 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    proxyTunnel: false,\n    headers: {\n      'User-Agent': 'Foobar/1.0.0'\n    }\n  })\n\n  proxy.on('connect', (req) => {\n    t.fail('proxy tunnel should not be established')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.headers['user-agent'], 'BarBaz/1.0.0')\n    res.end()\n  })\n\n  await request(serverUrl + '/hello?foo=bar', {\n    headers: { 'user-agent': 'BarBaz/1.0.0' },\n    dispatcher: proxyAgent\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with custom headers with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    headers: {\n      'User-Agent': 'Foobar/1.0.0'\n    },\n    proxyTunnel: true\n  })\n\n  proxy.on('connect', (req) => {\n    t.strictEqual(req.headers['user-agent'], 'Foobar/1.0.0')\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.headers['user-agent'], 'BarBaz/1.0.0')\n    res.end()\n  })\n\n  await request(serverUrl + '/hello?foo=bar', {\n    headers: { 'user-agent': 'BarBaz/1.0.0' },\n    dispatcher: proxyAgent\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('sending proxy-authorization in request headers should throw', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent(proxyUrl)\n\n  server.on('request', (req, res) => {\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  await t.rejects(\n    request(\n      serverUrl + '/hello?foo=bar',\n      {\n        dispatcher: proxyAgent,\n        headers: {\n          'proxy-authorization': Buffer.from('user:pass').toString('base64')\n        }\n      }\n    ),\n    'Proxy-Authorization should be sent in ProxyAgent'\n  )\n\n  await t.rejects(\n    request(\n      serverUrl + '/hello?foo=bar',\n      {\n        dispatcher: proxyAgent,\n        headers: {\n          'PROXY-AUTHORIZATION': Buffer.from('user:pass').toString('base64')\n        }\n      }\n    ),\n    'Proxy-Authorization should be sent in ProxyAgent'\n  )\n\n  await t.rejects(\n    request(\n      serverUrl + '/hello?foo=bar',\n      {\n        dispatcher: proxyAgent,\n        headers: {\n          'Proxy-Authorization': Buffer.from('user:pass').toString('base64')\n        }\n      }\n    ),\n    'Proxy-Authorization should be sent in ProxyAgent'\n  )\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with setGlobalDispatcher', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const defaultDispatcher = getGlobalDispatcher()\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n  const parsedOrigin = new URL(serverUrl)\n  setGlobalDispatcher(proxyAgent)\n\n  after(() => setGlobalDispatcher(defaultDispatcher))\n\n  proxy.on('connect', () => {\n    // proxyTunnel must be set to true in order to tunnel into the endpoint for HTTP->HTTP proxy connections\n    t.fail(true, 'connect to proxy should unreachable by default for HTTP->HTTP proxy connections')\n  })\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar')\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('use proxy-agent with setGlobalDispatcher with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 6 })\n  const defaultDispatcher = getGlobalDispatcher()\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  const parsedOrigin = new URL(serverUrl)\n  setGlobalDispatcher(proxyAgent)\n\n  after(() => setGlobalDispatcher(defaultDispatcher))\n\n  proxy.on('connect', () => {\n    t.ok(true, 'should call proxy')\n  })\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const {\n    statusCode,\n    headers,\n    body\n  } = await request(serverUrl + '/hello?foo=bar')\n  const json = await body.json()\n\n  t.strictEqual(statusCode, 200)\n  t.deepStrictEqual(json, { hello: 'world' })\n  t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('ProxyAgent correctly sends headers when using fetch - #1355, #1623', async (t) => {\n  t = tspl(t, { plan: 1 })\n  const defaultDispatcher = getGlobalDispatcher()\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n  setGlobalDispatcher(proxyAgent)\n\n  after(() => setGlobalDispatcher(defaultDispatcher))\n\n  const expectedHeaders = {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive',\n    'test-header': 'value',\n    accept: '*/*',\n    'accept-language': '*',\n    'sec-fetch-mode': 'cors',\n    'user-agent': 'undici',\n    'accept-encoding': 'gzip, deflate'\n  }\n\n  proxy.on('connect', (req, res) => {\n    // proxyTunnel must be set to true in order to tunnel into the endpoint for HTTP->HTTP proxy connections\n    t.fail(true, 'connect to proxy should unreachable by default for HTTP->HTTP proxy connections')\n  })\n\n  server.on('request', (req, res) => {\n    // The `proxy` package will add a \"via\" and \"x-forwarded-for\" header for non-tunneled Proxy requests\n    for (const header of ['via', 'x-forwarded-for']) {\n      delete req.headers[header]\n    }\n    t.deepStrictEqual(req.headers, expectedHeaders)\n    res.end('goodbye')\n  })\n\n  await fetch(serverUrl, {\n    headers: { 'Test-header': 'value' }\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n  t.end()\n})\n\ntest('ProxyAgent correctly sends headers when using fetch - #1355, #1623 (with proxy tunneling enabled)', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const defaultDispatcher = getGlobalDispatcher()\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  setGlobalDispatcher(proxyAgent)\n\n  after(() => setGlobalDispatcher(defaultDispatcher))\n\n  const expectedHeaders = {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive',\n    'test-header': 'value',\n    accept: '*/*',\n    'accept-language': '*',\n    'sec-fetch-mode': 'cors',\n    'user-agent': 'undici',\n    'accept-encoding': 'gzip, deflate'\n  }\n\n  const expectedProxyHeaders = {\n    'proxy-connection': 'keep-alive',\n    host: `localhost:${server.address().port}`,\n    connection: 'close'\n  }\n\n  proxy.on('connect', (req, res) => {\n    t.deepStrictEqual(req.headers, expectedProxyHeaders)\n  })\n\n  server.on('request', (req, res) => {\n    t.deepStrictEqual(req.headers, expectedHeaders)\n    res.end('goodbye')\n  })\n\n  await fetch(serverUrl, {\n    headers: { 'Test-header': 'value' }\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n  t.end()\n})\n\ntest('should throw when proxy does not return 200', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  proxy.on('connect', () => {\n    // proxyTunnel must be set to true in order to tunnel into the endpoint for HTTP->HTTP proxy connections\n    t.fail(true, 'connect to proxy should unreachable by default for HTTP->HTTP proxy connections')\n  })\n\n  proxy.authenticate = function (_req) {\n    t.ok(true, 'should call authenticate')\n    return false\n  }\n\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n  try {\n    await request(serverUrl, { dispatcher: proxyAgent })\n    t.fail()\n  } catch (e) {\n    t.ok(true, 'pass')\n    t.ok(e)\n  }\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n  await t.completed\n})\n\ntest('should throw when proxy does not return 200 with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  proxy.authenticate = function (_req) {\n    t.ok(true, 'should call authenticate')\n    return false\n  }\n\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  try {\n    await request(serverUrl, { dispatcher: proxyAgent })\n    t.fail()\n  } catch (e) {\n    t.ok(true, 'pass')\n    t.ok(e)\n  }\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n  await t.completed\n})\n\ntest('pass ProxyAgent proxy status code error when using fetch - #2161', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  proxy.authenticate = function (_req) {\n    t.ok(true, 'should call authenticate')\n    return false\n  }\n\n  const proxyAgent = new ProxyAgent(proxyUrl)\n  try {\n    await fetch(serverUrl, { dispatcher: proxyAgent })\n  } catch (e) {\n    t.ok('cause' in e)\n  }\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n\n  await t.completed\n})\n\ntest('Proxy via HTTP to HTTPS endpoint', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = await buildSSLServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `https://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    requestTls: {\n      ca: [\n        certs.root.crt\n      ],\n      servername: 'agent1'\n    }\n  })\n\n  server.on('request', function (req, res) {\n    t.ok(req.connection.encrypted)\n    res.end(JSON.stringify(req.headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.ok(true, 'server should be connected secured')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.fail('proxy over http should not call secureConnection')\n  })\n\n  proxy.on('connect', function () {\n    t.ok(true, 'proxy should be connected')\n  })\n\n  proxy.on('request', function () {\n    t.fail('proxy should never receive requests')\n  })\n\n  const data = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await data.body.json()\n  t.deepStrictEqual(json, {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive'\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('Proxy via HTTPS to HTTPS endpoint', async (t) => {\n  t = tspl(t, { plan: 5 })\n  const server = await buildSSLServer()\n  const proxy = await buildSSLProxy()\n\n  const serverUrl = `https://localhost:${server.address().port}`\n  const proxyUrl = `https://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    proxyTls: {\n      ca: [\n        certs.root.crt\n      ],\n      servername: 'proxy'\n    },\n    requestTls: {\n      ca: [\n        certs.root.crt\n      ],\n      servername: 'agent1'\n    }\n  })\n\n  server.on('request', function (req, res) {\n    t.ok(req.connection.encrypted)\n    res.end(JSON.stringify(req.headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.ok(true, 'server should be connected secured')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.ok(true, 'proxy over http should call secureConnection')\n  })\n\n  proxy.on('connect', function () {\n    t.ok(true, 'proxy should be connected')\n  })\n\n  proxy.on('request', function () {\n    t.fail('proxy should never receive requests')\n  })\n\n  const data = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await data.body.json()\n  t.deepStrictEqual(json, {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive'\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('Proxy via HTTPS to HTTP endpoint with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildSSLProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `https://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    proxyTls: {\n      ca: [\n        certs.root.crt\n      ],\n      servername: 'proxy'\n    },\n    proxyTunnel: true\n  })\n\n  server.on('request', function (req, res) {\n    t.ok(!req.connection.encrypted)\n    res.end(JSON.stringify(req.headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.fail('server is http')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.ok(true, 'proxy over http should call secureConnection')\n  })\n\n  proxy.on('request', function () {\n    t.fail('proxy should never receive requests')\n  })\n\n  const data = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await data.body.json()\n  t.deepStrictEqual(json, {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive'\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('Proxy via HTTP to HTTP endpoint with tunneling enabled', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n\n  server.on('request', function (req, res) {\n    t.ok(!req.connection.encrypted)\n    res.end(JSON.stringify(req.headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.fail('server is http')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.fail('proxy is http')\n  })\n\n  proxy.on('connect', () => {\n    t.ok(true, 'connect to proxy')\n  })\n\n  proxy.on('request', function () {\n    t.fail('proxy should never receive requests')\n  })\n\n  const data = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await data.body.json()\n  t.deepStrictEqual(json, {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive'\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('Proxy via HTTP to HTTP endpoint with tunneling disabled', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: false })\n\n  server.on('request', function (req, res) {\n    t.ok(!req.connection.encrypted)\n    const headers = { host: req.headers.host, connection: req.headers.connection }\n    res.end(JSON.stringify(headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.fail('server is http')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.fail('proxy is http')\n  })\n\n  proxy.on('connect', () => {\n    t.fail(true, 'connect to proxy should unreachable if proxyTunnel is false')\n  })\n\n  proxy.on('request', function (req) {\n    const bits = { method: req.method, url: req.url }\n    t.deepStrictEqual(bits, {\n      method: 'GET',\n      url: `${serverUrl}/`\n    })\n  })\n\n  const data = await request(serverUrl, { dispatcher: proxyAgent })\n  const json = await data.body.json()\n  t.deepStrictEqual(json, {\n    host: `localhost:${server.address().port}`,\n    connection: 'keep-alive'\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('Proxy via HTTPS to HTTP fails on wrong SNI', async (t) => {\n  t = tspl(t, { plan: 3 })\n  const server = await buildServer()\n  const proxy = await buildSSLProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `https://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({\n    uri: proxyUrl,\n    proxyTls: {\n      ca: [\n        certs.root.crt\n      ]\n    }\n  })\n\n  server.on('request', function (req, res) {\n    t.ok(!req.connection.encrypted)\n    res.end(JSON.stringify(req.headers))\n  })\n\n  server.on('secureConnection', () => {\n    t.fail('server is http')\n  })\n\n  proxy.on('secureConnection', () => {\n    t.fail('proxy is http')\n  })\n\n  proxy.on('connect', () => {\n    t.ok(true, 'connect to proxy')\n  })\n\n  proxy.on('request', function () {\n    t.fail('proxy should never receive requests')\n  })\n\n  try {\n    await request(serverUrl, { dispatcher: proxyAgent })\n    throw new Error('should fail')\n  } catch (e) {\n    t.ok(e instanceof SecureProxyConnectionError)\n    t.ok(e.cause instanceof Error)\n    t.ok(e.cause.code === 'ERR_TLS_CERT_ALTNAME_INVALID')\n  }\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('ProxyAgent keeps customized host in request headers - #3019', async (t) => {\n  t = tspl(t, { plan: 2 })\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n  const proxyAgent = new ProxyAgent({ uri: proxyUrl, proxyTunnel: true })\n  const customHost = 'example.com'\n\n  proxy.on('connect', (req) => {\n    t.strictEqual(req.headers.host, `localhost:${server.address().port}`)\n  })\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.headers.host, customHost)\n    res.end()\n  })\n\n  await request(serverUrl, {\n    headers: { Host: customHost },\n    dispatcher: proxyAgent\n  })\n\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\ntest('ProxyAgent handles multiple concurrent HTTP requests via HTTP proxy', async (t) => {\n  t = tspl(t, { plan: 20 })\n  // Start target HTTP server\n  const server = createServer((req, res) => {\n    setTimeout(() => {\n      res.setHeader('content-type', 'application/json')\n      res.end(JSON.stringify({ url: req.url }))\n    }, 50)\n  })\n  await new Promise(resolve => server.listen(0, resolve))\n  const targetPort = server.address().port\n\n  // Start HTTP proxy server\n  const proxy = createProxy(createServer())\n  await new Promise(resolve => proxy.listen(0, resolve))\n  const proxyPort = proxy.address().port\n\n  // Create ProxyAgent (no tunneling, plain HTTP)\n  const proxyAgent = new ProxyAgent(`http://localhost:${proxyPort}`)\n\n  const N = 10\n  const requests = []\n  for (let i = 0; i < N; i++) {\n    requests.push(\n      request(`http://localhost:${targetPort}/test${i}`, { dispatcher: proxyAgent })\n        .then(async res => {\n          t.strictEqual(res.statusCode, 200)\n          const json = await res.body.json()\n          t.deepStrictEqual(json, { url: `/test${i}` })\n        })\n    )\n  }\n  try {\n    await Promise.all(requests)\n  } catch (err) {\n    t.fail(err)\n  }\n  server.close()\n  proxy.close()\n  proxyAgent.close()\n})\n\nfunction buildServer () {\n  return new Promise((resolve) => {\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildSSLServer () {\n  const serverOptions = {\n    ca: [\n      certs.root.crt\n    ],\n    key: certs.server.key,\n    cert: certs.server.crt,\n    joinDuplicateHeaders: true\n  }\n  return new Promise((resolve) => {\n    const server = https.createServer(serverOptions)\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildProxy (listener) {\n  return new Promise((resolve) => {\n    const server = listener\n      ? createProxy(createServer(listener))\n      : createProxy(createServer({ joinDuplicateHeaders: true }))\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildSSLProxy () {\n  const serverOptions = {\n    ca: [\n      certs.root.crt\n    ],\n    key: certs.proxy.key,\n    cert: certs.proxy.crt,\n    joinDuplicateHeaders: true\n  }\n\n  return new Promise((resolve) => {\n    const server = createProxy(https.createServer(serverOptions))\n    server.listen(0, () => resolve(server))\n  })\n}\n"
  },
  {
    "path": "test/proxy.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { Client, Pool } = require('..')\nconst { createServer } = require('node:http')\nconst { createProxy } = require('proxy')\n\ntest('connect through proxy', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const client = new Client(proxyUrl)\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: serverUrl + '/hello?foo=bar'\n  })\n\n  response.body.setEncoding('utf8')\n  let data = ''\n  for await (const chunk of response.body) {\n    data += chunk\n  }\n  t.strictEqual(response.statusCode, 200)\n  t.deepStrictEqual(JSON.parse(data), { hello: 'world' })\n\n  server.close()\n  proxy.close()\n  client.close()\n})\n\ntest('connect through proxy with auth', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  proxy.authenticate = function (req) {\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`\n  }\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const client = new Client(proxyUrl)\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: serverUrl + '/hello?foo=bar',\n    headers: {\n      'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`\n    }\n  })\n\n  response.body.setEncoding('utf8')\n  let data = ''\n  for await (const chunk of response.body) {\n    data += chunk\n  }\n  t.strictEqual(response.statusCode, 200)\n  t.deepStrictEqual(JSON.parse(data), { hello: 'world' })\n\n  server.close()\n  proxy.close()\n  client.close()\n})\n\ntest('connect through proxy with auth but invalid credentials', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  proxy.authenticate = function (req) {\n    return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:no-pass').toString('base64')}`\n  }\n\n  server.on('request', (req, res) => {\n    t.fail('should not be called')\n  })\n\n  const client = new Client(proxyUrl)\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const response = await client.request({\n    method: 'GET',\n    path: serverUrl + '/hello?foo=bar',\n    headers: {\n      'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`\n    }\n  })\n\n  t.strictEqual(response.statusCode, 407)\n\n  server.close()\n  proxy.close()\n  client.close()\n})\n\ntest('connect through proxy (with pool)', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = await buildServer()\n  const proxy = await buildProxy()\n\n  const serverUrl = `http://localhost:${server.address().port}`\n  const proxyUrl = `http://localhost:${proxy.address().port}`\n\n  server.on('request', (req, res) => {\n    t.strictEqual(req.url, '/hello?foo=bar')\n    res.setHeader('content-type', 'application/json')\n    res.end(JSON.stringify({ hello: 'world' }))\n  })\n\n  const pool = new Pool(proxyUrl)\n\n  pool.on('disconnect', () => {\n    if (!pool.closed && !pool.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const response = await pool.request({\n    method: 'GET',\n    path: serverUrl + '/hello?foo=bar'\n  })\n\n  response.body.setEncoding('utf8')\n  let data = ''\n  for await (const chunk of response.body) {\n    data += chunk\n  }\n  t.strictEqual(response.statusCode, 200)\n  t.deepStrictEqual(JSON.parse(data), { hello: 'world' })\n\n  server.close()\n  proxy.close()\n  pool.close()\n})\n\nfunction buildServer () {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true })\n    server.listen(0, () => resolve(server))\n  })\n}\n\nfunction buildProxy () {\n  return new Promise((resolve, reject) => {\n    const server = createProxy(createServer({ joinDuplicateHeaders: true }))\n    server.listen(0, () => resolve(server))\n  })\n}\n"
  },
  {
    "path": "test/readable.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, describe } = require('node:test')\nconst { Readable } = require('../lib/api/readable')\n\ndescribe('Readable', () => {\n  test('avoid body reordering', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('hello'))\n\n    process.nextTick(() => {\n      r.push(Buffer.from('world'))\n      r.push(null)\n    })\n\n    const text = await r.text()\n\n    t.strictEqual(text, 'helloworld')\n  })\n\n  test('destroy timing text', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n\n    const r = new Readable({ resume, abort })\n    r.destroy(new Error('kaboom'))\n\n    await t.rejects(r.text(), new Error('kaboom'))\n  })\n\n  test('destroy timing promise', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = await new Promise(resolve => {\n      const r = new Readable({ resume, abort })\n      r.destroy(new Error('kaboom'))\n      resolve(r)\n    })\n    await new Promise(resolve => {\n      r.on('error', err => {\n        t.ok(err)\n        resolve(null)\n      })\n    })\n  })\n\n  test('.arrayBuffer()', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('hello world'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    const arrayBuffer = await r.arrayBuffer()\n\n    const expected = new ArrayBuffer(11)\n    const view = new Uint8Array(expected)\n    view.set(Buffer.from('hello world'))\n    t.deepStrictEqual(arrayBuffer, expected)\n  })\n\n  test('.bytes()', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('hello'))\n    r.push(Buffer.from(' world'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    const bytes = await r.bytes()\n\n    t.deepStrictEqual(bytes, new TextEncoder().encode('hello world'))\n  })\n\n  test('.json()', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('{\"hello\": \"world\"}'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    const obj = await r.json()\n\n    t.deepStrictEqual(obj, { hello: 'world' })\n  })\n\n  test('.text()', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('hello world'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    const text = await r.text()\n\n    t.strictEqual(text, 'hello world')\n  })\n\n  test('ignore BOM', async function (t) {\n    t = tspl(t, { plan: 1 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push('\\uFEFF')\n    r.push(Buffer.from('hello world'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    const text = await r.text()\n\n    t.strictEqual(text, 'hello world')\n  })\n\n  test('.bodyUsed', async function (t) {\n    t = tspl(t, { plan: 3 })\n\n    function resume () {\n    }\n    function abort () {\n    }\n    const r = new Readable({ resume, abort })\n\n    r.push(Buffer.from('hello world'))\n\n    process.nextTick(() => {\n      r.push(null)\n    })\n\n    t.strictEqual(r.bodyUsed, false)\n\n    const text = await r.text()\n\n    t.strictEqual(r.bodyUsed, true)\n\n    t.strictEqual(text, 'hello world')\n  })\n})\n"
  },
  {
    "path": "test/redirect-pipeline.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { pipeline: undiciPipeline, Client, interceptors } = require('..')\nconst { pipeline: streamPipelineCb } = require('node:stream')\nconst { promisify } = require('node:util')\nconst { createReadable, createWritable } = require('./utils/stream')\nconst { startRedirectingServer } = require('./utils/redirecting-servers')\n\nconst streamPipeline = promisify(streamPipelineCb)\nconst redirect = interceptors.redirect\n\ntest('should not follow redirection by default if not using RedirectAgent', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const serverRoot = await startRedirectingServer()\n\n  await streamPipeline(\n    createReadable('REQUEST'),\n    undiciPipeline(`http://${serverRoot}/`, {\n      dispatcher: new Client(`http://${serverRoot}/`).compose(redirect({ maxRedirections: null }))\n    }, ({ statusCode, headers, body }) => {\n      t.strictEqual(statusCode, 302)\n      t.strictEqual(headers.location, `http://${serverRoot}/302/1`)\n\n      return body\n    }),\n    createWritable(body)\n  )\n\n  t.strictEqual(body.length, 0)\n})\n\ntest('should not follow redirects when using RedirectAgent within pipeline', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const serverRoot = await startRedirectingServer()\n\n  await streamPipeline(\n    createReadable('REQUEST'),\n    undiciPipeline(`http://${serverRoot}/`, { dispatcher: new Client(`http://${serverRoot}/`).compose(redirect({ maxRedirections: 1 })) }, ({ statusCode, headers, body }) => {\n      t.strictEqual(statusCode, 302)\n      t.strictEqual(headers.location, `http://${serverRoot}/302/1`)\n\n      return body\n    }),\n    createWritable(body)\n  )\n\n  t.strictEqual(body.length, 0)\n})\n"
  },
  {
    "path": "test/redirect-request.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst undici = require('..')\nconst {\n  startRedirectingServer,\n  startRedirectingWithBodyServer,\n  startRedirectingChainServers,\n  startRedirectingWithoutLocationServer,\n  startRedirectingWithAuthorization,\n  startRedirectingWithCookie,\n  startRedirectingWithQueryParams\n} = require('./utils/redirecting-servers')\nconst { createReadable, createReadableStream } = require('./utils/stream')\nconst { Headers: UndiciHeaders } = require('..')\nconst redirect = undici.interceptors.redirect\n\nfor (const factory of [\n  (server, opts) => new undici.Agent(opts).compose(redirect({ maxRedirections: opts?.maxRedirections })),\n  (server, opts) => new undici.Pool(`http://${server}`, opts).compose(redirect({ maxRedirections: opts?.maxRedirections })),\n  (server, opts) => new undici.Client(`http://${server}`, opts).compose(redirect({ maxRedirections: opts?.maxRedirections }))\n]) {\n  const request = (t, server, opts, ...args) => {\n    const dispatcher = factory(server, opts)\n    after(() => dispatcher.close())\n    return undici.request(args[0], { ...args[1], dispatcher }, args[2])\n  }\n\n  test('should always have a history with the final URL even if no redirections were followed', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream, context: { history } } = await request(t, server, undefined, `http://${server}/200?key=value`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/200?key=value`])\n    t.strictEqual(body, `GET /5 key=value :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should not follow redirection by default if not using RedirectAgent', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}`)\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 302)\n    t.strictEqual(headers.location, `http://${server}/302/1`)\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 300', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream, context: { history } } = await request(t, server, undefined, `http://${server}/300?key=value`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(history.map(x => x.toString()), [\n      `http://${server}/300?key=value`,\n      `http://${server}/300/1?key=value`,\n      `http://${server}/300/2?key=value`,\n      `http://${server}/300/3?key=value`,\n      `http://${server}/300/4?key=value`,\n      `http://${server}/300/5?key=value`\n    ])\n    t.strictEqual(body, `GET /5 key=value :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 300 default', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream, context: { history } } = await request(t, server, undefined, `http://${server}/300?key=value`, { maxRedirections: 10 })\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(history.map(x => x.toString()), [\n      `http://${server}/300?key=value`,\n      `http://${server}/300/1?key=value`,\n      `http://${server}/300/2?key=value`,\n      `http://${server}/300/3?key=value`,\n      `http://${server}/300/4?key=value`,\n      `http://${server}/300/5?key=value`\n    ])\n    t.strictEqual(body, `GET /5 key=value :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 301', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'POST',\n      body: 'REQUEST',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive`)\n  })\n\n  test('should follow redirection after a HTTP 302', async t => {\n    t = tspl(t, { plan: 3 })\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/302`, {\n      method: 'PUT',\n      body: Buffer.from('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `PUT /5 :: host@${server} connection@keep-alive content-length@7 :: REQUEST`)\n  })\n\n  test('should follow redirection after a HTTP 303 changing method to GET', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      body: 'REQUEST',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (array)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: [\n        'Content-Encoding',\n        'gzip',\n        'X-Foo1',\n        '1',\n        'X-Foo2',\n        '2',\n        'Content-Type',\n        'application/json',\n        'X-Foo3',\n        '3',\n        'Host',\n        'localhost',\n        'X-Bar',\n        '4'\n      ],\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (object)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: {\n        'Content-Encoding': 'gzip',\n        'X-Foo1': '1',\n        'X-Foo2': '2',\n        'Content-Type': 'application/json',\n        'X-Foo3': '3',\n        Host: 'localhost',\n        'X-Bar': '4'\n      },\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (Global Headers)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      // eslint-disable-next-line no-restricted-globals\n      headers: new Headers({\n        'Content-Encoding': 'gzip',\n        'X-Foo1': '1',\n        'X-Foo2': '2',\n        'Content-Type': 'application/json',\n        'X-Foo3': '3',\n        Host: 'localhost',\n        'X-Bar': '4'\n      }),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive x-bar@4 x-foo1@1 x-foo2@2 x-foo3@3`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (Undici Headers)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: new UndiciHeaders({\n        'Content-Encoding': 'gzip',\n        'X-Foo1': '1',\n        'X-Foo2': '2',\n        'Content-Type': 'application/json',\n        'X-Foo3': '3',\n        Host: 'localhost',\n        'X-Bar': '4'\n      }),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive x-bar@4 x-foo1@1 x-foo2@2 x-foo3@3`)\n\n    await t.completed\n  })\n\n  test('should remove Host and request body related headers when following HTTP 303 (Maps)', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/303`, {\n      method: 'PATCH',\n      headers: new Map([\n        ['Content-Encoding', 'gzip'],\n        ['X-Foo1', '1'],\n        ['X-Foo2', '2'],\n        ['Content-Type', 'application/json'],\n        ['X-Foo3', '3'],\n        ['Host', 'localhost'],\n        ['X-Bar', '4']\n      ]),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 307', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/307`, {\n      method: 'DELETE',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `DELETE /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should follow redirection after a HTTP 308', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/308`, {\n      method: 'OPTIONS',\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.strictEqual(body, `OPTIONS /5 :: host@${server} connection@keep-alive`)\n\n    await t.completed\n  })\n\n  test('should ignore HTTP 3xx response bodies', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingWithBodyServer()\n\n    const { statusCode, headers, body: bodyStream, context: { history } } = await request(t, server, undefined, `http://${server}/`, {\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/`, `http://${server}/end`])\n    t.strictEqual(body, 'FINAL')\n\n    await t.completed\n  })\n\n  test('should ignore query after redirection', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingWithQueryParams()\n\n    const { statusCode, headers, context: { history } } = await request(t, server, undefined, `http://${server}/`, {\n      maxRedirections: 10,\n      query: { param1: 'first' }\n    })\n\n    t.strictEqual(statusCode, 200)\n    t.ok(!headers.location)\n    t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/`, `http://${server}/?param2=second`])\n\n    await t.completed\n  })\n\n  test('should follow a redirect chain up to the allowed number of times', async t => {\n    t = tspl(t, { plan: 4 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream, context: { history } } = await request(t, server, undefined, `http://${server}/300`, {\n      maxRedirections: 2\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 300)\n    t.strictEqual(headers.location, `http://${server}/300/3`)\n    t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/300`, `http://${server}/300/1`, `http://${server}/300/2`])\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should follow a redirect chain up to the allowed number of times for redirectionLimitReached', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = await startRedirectingServer()\n\n    try {\n      await request(t, server, undefined, `http://${server}/300`, {\n        maxRedirections: 2,\n        throwOnMaxRedirect: true\n      })\n    } catch (error) {\n      if (error.message.startsWith('max redirects')) {\n        t.ok(true, 'Max redirects handled correctly')\n      } else {\n        t.fail(`Unexpected error: ${error.message}`)\n      }\n    }\n\n    await t.completed\n  })\n\n  test('when a Location response header is NOT present', async t => {\n    t = tspl(t, { plan: 6 * 3 })\n\n    const redirectCodes = [300, 301, 302, 303, 307, 308]\n    const server = await startRedirectingWithoutLocationServer()\n\n    for (const code of redirectCodes) {\n      const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/${code}`, {\n        maxRedirections: 10\n      })\n\n      const body = await bodyStream.text()\n\n      t.strictEqual(statusCode, code)\n      t.ok(!headers.location)\n      t.strictEqual(body.length, 0)\n    }\n    await t.completed\n  })\n\n  test('should not allow invalid maxRedirections arguments', async t => {\n    t = tspl(t, { plan: 1 })\n\n    try {\n      await request(t, 'localhost', undefined, 'http://localhost', {\n        method: 'GET',\n        maxRedirections: 'INVALID'\n      })\n\n      t.fail('Did not throw')\n    } catch (err) {\n      t.strictEqual(err.message, 'maxRedirections must be a positive number')\n    }\n    await t.completed\n  })\n\n  test('should not allow invalid maxRedirections arguments default', async t => {\n    t = tspl(t, { plan: 1 })\n\n    try {\n      await request(t, 'localhost', undefined, 'http://localhost', {\n        method: 'GET',\n        maxRedirections: 'INVALID'\n      })\n\n      t.fail('Did not throw')\n    } catch (err) {\n      t.strictEqual(err.message, 'maxRedirections must be a positive number')\n    }\n\n    await t.completed\n  })\n\n  test('should not follow redirects when using ReadableStream request bodies', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'PUT',\n      body: createReadableStream('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 301)\n    t.strictEqual(headers.location, `http://${server}/301/2`)\n    t.strictEqual(body.length, 0)\n\n    await t.completed\n  })\n\n  test('should not follow redirects when using Readable request bodies', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'PUT',\n      body: createReadable('REQUEST'),\n      maxRedirections: 10\n    })\n\n    const body = await bodyStream.text()\n\n    t.strictEqual(statusCode, 301)\n    t.strictEqual(headers.location, `http://${server}/301/1`)\n    t.strictEqual(body.length, 0)\n    await t.completed\n  })\n\n  test('should follow redirects when using Readable request bodies for POST 301', async t => {\n    t = tspl(t, { plan: 1 })\n\n    const server = await startRedirectingServer()\n\n    const { statusCode, body: bodyStream } = await request(t, server, undefined, `http://${server}/301`, {\n      method: 'POST',\n      body: createReadable('REQUEST'),\n      maxRedirections: 10\n    })\n\n    await bodyStream.text()\n\n    t.strictEqual(statusCode, 200)\n    await t.completed\n  })\n}\n\ntest('should follow redirections when going cross origin', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const [server1, server2, server3] = await startRedirectingChainServers()\n\n  const { statusCode, headers, body: bodyStream, context: { history } } = await undici.request(`http://${server1}`, {\n    method: 'POST',\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 }))\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(statusCode, 200)\n  t.ok(!headers.location)\n  t.deepStrictEqual(history.map(x => x.toString()), [\n    `http://${server1}/`,\n    `http://${server2}/`,\n    `http://${server3}/`,\n    `http://${server2}/end`,\n    `http://${server3}/end`,\n    `http://${server1}/end`\n  ])\n  t.strictEqual(body, 'GET')\n\n  await t.completed\n})\n\ntest('should handle errors (callback)', async t => {\n  t = tspl(t, { plan: 1 })\n\n  undici.request(\n    'http://localhost:0',\n    {\n      dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 }))\n    },\n    error => {\n      t.match(error.code, /EADDRNOTAVAIL|ECONNREFUSED/)\n    }\n  )\n\n  await t.completed\n})\n\ntest('should handle errors (promise)', async t => {\n  t = tspl(t, { plan: 1 })\n\n  try {\n    await undici.request('http://localhost:0', { dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 })) })\n    t.fail('Did not throw')\n  } catch (error) {\n    t.match(error.code, /EADDRNOTAVAIL|ECONNREFUSED/)\n  }\n\n  await t.completed\n})\n\ntest('removes authorization header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const [server1] = await startRedirectingWithAuthorization('secret')\n  const { body: bodyStream } = await undici.request(`http://${server1}`, {\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 })),\n    headers: {\n      authorization: 'secret'\n    }\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(body, '')\n\n  await t.completed\n})\n\ntest('removes cookie header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n  const [server1] = await startRedirectingWithCookie('a=b')\n  const { body: bodyStream } = await undici.request(`http://${server1}`, {\n    dispatcher: new undici.Agent({}).compose(redirect({ maxRedirections: 10 })),\n    headers: {\n      cookie: 'a=b'\n    }\n  })\n\n  const body = await bodyStream.text()\n\n  t.strictEqual(body, '')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/redirect-stream.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, describe } = require('node:test')\nconst { stream, Agent, Client, interceptors: { redirect } } = require('..')\nconst {\n  startRedirectingServer,\n  startRedirectingWithBodyServer,\n  startRedirectingChainServers,\n  startRedirectingWithoutLocationServer,\n  startRedirectingWithAuthorization,\n  startRedirectingWithCookie\n} = require('./utils/redirecting-servers')\nconst { createReadable, createWritable } = require('./utils/stream')\n\ntest('should always have a history with the final URL even if no redirections were followed', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/200?key=value`,\n    { opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque, context: { history } }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n      t.deepStrictEqual(history.map(x => x.toString()), [\n        `http://${server}/200?key=value`\n      ])\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `GET /5 key=value :: host@${server} connection@keep-alive`)\n})\n\ntest('should not follow redirection by default if max redirect = 0', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(`http://${server}`, { opaque: body, dispatcher: new Agent({}).compose(redirect({ maxRedirections: 0 })) }, ({ statusCode, headers, opaque }) => {\n    t.strictEqual(statusCode, 302)\n    t.strictEqual(headers.location, `http://${server}/302/1`)\n\n    return createWritable(opaque)\n  })\n\n  t.strictEqual(body.length, 0)\n})\n\ntest('should follow redirection after a HTTP 300', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/300?key=value`,\n    { opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque, context: { history } }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n      t.deepStrictEqual(history.map(x => x.toString()), [\n        `http://${server}/300?key=value`,\n        `http://${server}/300/1?key=value`,\n        `http://${server}/300/2?key=value`,\n        `http://${server}/300/3?key=value`,\n        `http://${server}/300/4?key=value`,\n        `http://${server}/300/5?key=value`\n      ])\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `GET /5 key=value :: host@${server} connection@keep-alive`)\n})\n\ntest('should follow redirection after a HTTP 301 changing method to GET', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/301`,\n    { method: 'POST', body: 'REQUEST', opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `GET /5 :: host@${server} connection@keep-alive`)\n})\n\ntest('should follow redirection after a HTTP 302', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/302`,\n    { method: 'PUT', body: Buffer.from('REQUEST'), opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `PUT /5 :: host@${server} connection@keep-alive content-length@7 :: REQUEST`)\n})\n\ntest('should follow redirection after a HTTP 303 changing method to GET', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(`http://${server}/303`, { opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) }, ({ statusCode, headers, opaque }) => {\n    t.strictEqual(statusCode, 200)\n    t.strictEqual(headers.location, undefined)\n\n    return createWritable(opaque)\n  })\n\n  t.strictEqual(body.join(''), `GET /5 :: host@${server} connection@keep-alive`)\n})\n\ntest('should remove Host and request body related headers when following HTTP 303 (array)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/303`,\n    {\n      method: 'PATCH',\n      headers: [\n        'Content-Encoding',\n        'gzip',\n        'X-Foo1',\n        '1',\n        'X-Foo2',\n        '2',\n        'Content-Type',\n        'application/json',\n        'X-Foo3',\n        '3',\n        'Host',\n        'localhost',\n        'X-Bar',\n        '4'\n      ],\n      opaque: body,\n      dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 }))\n    },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`)\n})\n\ntest('should remove Host and request body related headers when following HTTP 303 (object)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/303`,\n    {\n      method: 'PATCH',\n      headers: {\n        'Content-Encoding': 'gzip',\n        'X-Foo1': '1',\n        'X-Foo2': '2',\n        'Content-Type': 'application/json',\n        'X-Foo3': '3',\n        Host: 'localhost',\n        'X-Bar': '4'\n      },\n      opaque: body,\n      dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 }))\n    },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `GET /5 :: host@${server} connection@keep-alive x-foo1@1 x-foo2@2 x-foo3@3 x-bar@4`)\n})\n\ntest('should follow redirection after a HTTP 307', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/307`,\n    { method: 'DELETE', opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `DELETE /5 :: host@${server} connection@keep-alive`)\n})\n\ntest('should follow redirection after a HTTP 308', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/308`,\n    { method: 'OPTIONS', opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), `OPTIONS /5 :: host@${server} connection@keep-alive`)\n})\n\ntest('should ignore HTTP 3xx response bodies', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const body = []\n  const server = await startRedirectingWithBodyServer()\n\n  await stream(\n    `http://${server}/`,\n    { opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque, context: { history } }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n      t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/`, `http://${server}/end`])\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), 'FINAL')\n})\n\ntest('should follow a redirect chain up to the allowed number of times', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}/300`,\n    { opaque: body, dispatcher: new Client(`http://${server}/`).compose(redirect({ maxRedirections: 2 })) },\n    ({ statusCode, headers, opaque, context: { history } }) => {\n      t.strictEqual(statusCode, 300)\n      t.strictEqual(headers.location, `http://${server}/300/3`)\n      t.deepStrictEqual(history.map(x => x.toString()), [`http://${server}/300`, `http://${server}/300/1`, `http://${server}/300/2`])\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.length, 0)\n})\n\ntest('should follow redirections when going cross origin', async t => {\n  t = tspl(t, { plan: 4 })\n\n  const [server1, server2, server3] = await startRedirectingChainServers()\n  const body = []\n\n  await stream(\n    `http://${server1}`,\n    { method: 'POST', opaque: body, dispatcher: new Agent({}).compose(redirect({ maxRedirections: 10 })) },\n    ({ statusCode, headers, opaque, context: { history } }) => {\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers.location, undefined)\n      t.deepStrictEqual(history.map(x => x.toString()), [\n        `http://${server1}/`,\n        `http://${server2}/`,\n        `http://${server3}/`,\n        `http://${server2}/end`,\n        `http://${server3}/end`,\n        `http://${server1}/end`\n      ])\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.join(''), 'GET')\n})\n\ndescribe('when a Location response header is NOT present', async () => {\n  const redirectCodes = [300, 301, 302, 303, 307, 308]\n  const server = await startRedirectingWithoutLocationServer()\n\n  for (const code of redirectCodes) {\n    test(`should return the original response after a HTTP ${code}`, async t => {\n      t = tspl(t, { plan: 3 })\n\n      const body = []\n\n      await stream(\n        `http://${server}/${code}`,\n        { opaque: body },\n        ({ statusCode, headers, opaque }) => {\n          t.strictEqual(statusCode, code)\n          t.strictEqual(headers.location, undefined)\n\n          return createWritable(opaque)\n        }\n      )\n\n      t.strictEqual(body.length, 0)\n      await t.completed\n    })\n  }\n})\n\ntest('should not follow redirects when using Readable request bodies', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const body = []\n  const server = await startRedirectingServer()\n\n  await stream(\n    `http://${server}`,\n    {\n      method: 'POST',\n      body: createReadable('REQUEST'),\n      opaque: body\n    },\n    ({ statusCode, headers, opaque }) => {\n      t.strictEqual(statusCode, 302)\n      t.strictEqual(headers.location, `http://${server}/302/1`)\n\n      return createWritable(opaque)\n    }\n  )\n\n  t.strictEqual(body.length, 0)\n})\n\ntest('should handle errors', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const body = []\n\n  try {\n    await stream('http://localhost:0', { opaque: body }, ({ statusCode, headers, opaque }) => {\n      return createWritable(opaque)\n    })\n\n    throw new Error('Did not throw')\n  } catch (error) {\n    t.match(error.code, /EADDRNOTAVAIL|ECONNREFUSED/)\n    t.strictEqual(body.length, 0)\n  }\n})\n\ntest('removes authorization header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const body = []\n\n  const [server1] = await startRedirectingWithAuthorization('secret')\n  await stream(`http://${server1}`, {\n    opaque: body,\n    headers: {\n      authorization: 'secret'\n    }\n  }, ({ statusCode, headers, opaque }) => createWritable(opaque))\n\n  t.strictEqual(body.length, 0)\n})\n\ntest('removes cookie header on third party origin', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const body = []\n\n  const [server1] = await startRedirectingWithCookie('a=b')\n  await stream(`http://${server1}`, {\n    opaque: body,\n    headers: {\n      cookie: 'a=b'\n    }\n  }, ({ statusCode, headers, opaque }) => createWritable(opaque))\n\n  t.strictEqual(body.length, 0)\n})\n"
  },
  {
    "path": "test/request-crlf.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { createServer } = require('node:http')\nconst { test, after } = require('node:test')\nconst { request, errors } = require('..')\nconst { once } = require('node:events')\n\ntest('should validate content-type CRLF Injection', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail('should not receive any request')\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  try {\n    await request(`http://localhost:${server.address().port}`, {\n      method: 'GET',\n      headers: {\n        'content-type': 'application/json\\r\\n\\r\\nGET /foo2 HTTP/1.1'\n      }\n    })\n    t.fail('request should fail')\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid content-type header')\n  }\n  await t.completed\n})\n"
  },
  {
    "path": "test/request-signal.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { test, after } = require('node:test')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { request } = require('..')\n\ntest('pre abort signal w/ reason', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const ac = new AbortController()\n    const _err = new Error()\n    ac.abort(_err)\n    try {\n      await request(`http://0.0.0.0:${server.address().port}`, { signal: ac.signal })\n    } catch (err) {\n      t.equal(err, _err)\n    }\n  })\n  await t.completed\n})\n\ntest('post abort signal', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const ac = new AbortController()\n    const ures = await request(`http://0.0.0.0:${server.address().port}`, { signal: ac.signal })\n    ac.abort()\n    try {\n      /* eslint-disable-next-line no-unused-vars */\n      for await (const chunk of ures.body) {\n        // Do nothing...\n      }\n    } catch (err) {\n      t.equal(err.name, 'AbortError')\n    }\n  })\n  await t.completed\n})\n\ntest('post abort signal w/ reason', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('asd')\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const ac = new AbortController()\n    const _err = new Error()\n    const ures = await request(`http://0.0.0.0:${server.address().port}`, { signal: ac.signal })\n    ac.abort(_err)\n    try {\n      /* eslint-disable-next-line no-unused-vars */\n      for await (const chunk of ures.body) {\n        // Do nothing...\n      }\n    } catch (err) {\n      t.equal(err, _err)\n    }\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/request-timeout.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { resolve: pathResolve } = require('node:path')\nconst { test, after, beforeEach } = require('node:test')\nconst { createReadStream, writeFileSync, unlinkSync } = require('node:fs')\nconst { Client, errors } = require('..')\nconst { kConnect } = require('../lib/core/symbols')\nconst { createServer } = require('node:http')\nconst EventEmitter = require('node:events')\nconst FakeTimers = require('@sinonjs/fake-timers')\nconst { AbortController } = require('abort-controller')\nconst {\n  pipeline,\n  Readable,\n  Writable,\n  PassThrough\n} = require('node:stream')\nconst {\n  tick: fastTimersTick,\n  reset: resetFastTimers\n} = require('../lib/util/timers')\n\nbeforeEach(() => {\n  resetFastTimers()\n})\n\ntest('request timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 2000)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { headersTimeout: 500 })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('request timeout with readable body', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  const tempfile = pathResolve(__dirname, 'request-timeout.10mb.bin')\n  writeFileSync(tempfile, Buffer.alloc(10 * 1024 * 1024))\n  after(() => unlinkSync(tempfile))\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { headersTimeout: 1e3 })\n    after(() => client.destroy())\n\n    const body = createReadStream(tempfile)\n    client.request({ path: '/', method: 'POST', body }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('body timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { bodyTimeout: 50 })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, { body }) => {\n      t.ifError(err)\n      body.on('data', () => {\n        clock.tick(100)\n        fastTimersTick(100)\n      }).on('error', (err) => {\n        t.ok(err instanceof errors.BodyTimeoutError)\n      })\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('overridden request timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(100)\n    fastTimersTick(100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { headersTimeout: 500 })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', headersTimeout: 50 }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('overridden body timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.write('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { bodyTimeout: 500 })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', bodyTimeout: 50 }, (err, { body }) => {\n      t.ifError(err)\n      body.on('data', () => {\n        fastTimersTick()\n        fastTimersTick()\n      }).on('error', (err) => {\n        t.ok(err instanceof errors.BodyTimeoutError)\n      })\n    })\n\n    fastTimersTick()\n    fastTimersTick()\n  })\n\n  await t.completed\n})\n\ntest('With EE signal', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(100)\n    fastTimersTick(100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    const ee = new EventEmitter()\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('With abort-controller signal', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(100)\n    fastTimersTick(100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    const abortController = new AbortController()\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('Abort before timeout (EE)', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const ee = new EventEmitter()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    ee.emit('abort')\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => {\n      t.ok(err instanceof errors.RequestAbortedError)\n      clock.tick(100)\n      fastTimersTick(100)\n    })\n  })\n\n  await t.completed\n})\n\ntest('Abort before timeout (abort-controller)', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const abortController = new AbortController()\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    abortController.abort()\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => {\n      t.ok(err instanceof errors.RequestAbortedError)\n      clock.tick(100)\n      fastTimersTick(100)\n    })\n  })\n\n  await t.completed\n})\n\ntest('Timeout with pipelining', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 10,\n      headersTimeout: 50\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('Global option', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(100)\n    fastTimersTick(100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('Request options overrides global option', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 100)\n    clock.tick(100)\n    fastTimersTick(100)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 50\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    clock.tick(50)\n    fastTimersTick(50)\n  })\n\n  await t.completed\n})\n\ntest('client.destroy should cancel the timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 100\n    })\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.ClientDestroyedError)\n    })\n\n    client.destroy(err => {\n      t.ifError(err)\n    })\n  })\n\n  await t.completed\n})\n\ntest('client.close should wait for the timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 100\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n\n    client.close((err) => {\n      t.ifError(err)\n    })\n\n    client.on('connect', () => {\n      process.nextTick(() => {\n        clock.tick(100)\n        fastTimersTick(100)\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('Validation', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  try {\n    const client = new Client('http://localhost:3000', {\n      headersTimeout: 'foobar'\n    })\n    after(() => client.destroy())\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n\n  try {\n    const client = new Client('http://localhost:3000', {\n      headersTimeout: -1\n    })\n    after(() => client.destroy())\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n\n  try {\n    const client = new Client('http://localhost:3000', {\n      bodyTimeout: 'foobar'\n    })\n    after(() => client.destroy())\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n\n  try {\n    const client = new Client('http://localhost:3000', {\n      bodyTimeout: -1\n    })\n    after(() => client.destroy())\n  } catch (err) {\n    t.ok(err instanceof errors.InvalidArgumentError)\n  }\n\n  await t.completed\n})\n\ntest('Disable request timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 32e3)\n    clock.tick(33e3)\n    fastTimersTick(33e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 0,\n      connectTimeout: 0\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ifError(err)\n      const bufs = []\n      response.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      response.body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    clock.tick(31e3)\n    fastTimersTick(31e3)\n  })\n\n  await t.completed\n})\n\ntest('Disable request timeout for a single request', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 32e3)\n    clock.tick(33e3)\n    fastTimersTick(33e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 0,\n      connectTimeout: 0\n    })\n    after(() => client.destroy())\n\n    client.request({ path: '/', method: 'GET' }, (err, response) => {\n      t.ifError(err)\n      const bufs = []\n      response.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      response.body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n\n    clock.tick(31e3)\n    fastTimersTick(31e3)\n  })\n\n  await t.completed\n})\n\ntest('stream timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 301e3)\n    clock.tick(301e3)\n    fastTimersTick(301e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, { connectTimeout: 0 })\n    after(() => client.destroy())\n\n    client.stream({\n      path: '/',\n      method: 'GET',\n      opaque: new PassThrough()\n    }, (result) => {\n      t.fail('Should not be called')\n    }, (err) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('stream custom timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 31e3)\n    clock.tick(31e3)\n    fastTimersTick(31e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 30e3\n    })\n    after(() => client.destroy())\n\n    client.stream({\n      path: '/',\n      method: 'GET',\n      opaque: new PassThrough()\n    }, (result) => {\n      t.fail('Should not be called')\n    }, (err) => {\n      t.ok(err instanceof errors.HeadersTimeoutError)\n    })\n  })\n\n  await t.completed\n})\n\ntest('pipeline timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      req.pipe(res)\n    }, 301e3)\n    clock.tick(301e3)\n    fastTimersTick(301e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const buf = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf)\n          this.push(null)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, (result) => {\n        t.fail('Should not be called')\n      }, (e) => {\n        t.fail('Should not be called')\n      }),\n      new Writable({\n        write (chunk, encoding, callback) {\n          callback()\n        },\n        final (callback) {\n          callback()\n        }\n      }),\n      (err) => {\n        t.ok(err instanceof errors.HeadersTimeoutError)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('pipeline timeout', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    setTimeout(() => {\n      req.pipe(res)\n    }, 31e3)\n    clock.tick(31e3)\n    fastTimersTick(31e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      headersTimeout: 30e3\n    })\n    after(() => client.destroy())\n\n    const buf = Buffer.alloc(1e6).toString()\n    pipeline(\n      new Readable({\n        read () {\n          this.push(buf)\n          this.push(null)\n        }\n      }),\n      client.pipeline({\n        path: '/',\n        method: 'PUT'\n      }, (result) => {\n        t.fail('Should not be called')\n      }, (e) => {\n        t.fail('Should not be called')\n      }),\n      new Writable({\n        write (chunk, encoding, callback) {\n          callback()\n        },\n        final (callback) {\n          callback()\n        }\n      }),\n      (err) => {\n        t.ok(err instanceof errors.HeadersTimeoutError)\n      }\n    )\n  })\n\n  await t.completed\n})\n\ntest('client.close should not deadlock', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const clock = FakeTimers.install({\n    shouldClearNativeTimers: true,\n    toFake: ['setTimeout', 'clearTimeout']\n  })\n  after(() => clock.uninstall())\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 200,\n      headersTimeout: 100\n    })\n    after(() => client.destroy())\n\n    client[kConnect](() => {\n      client.request({\n        path: '/',\n        method: 'GET'\n      }, (err, response) => {\n        t.ok(err instanceof errors.HeadersTimeoutError)\n      })\n\n      client.close((err) => {\n        t.ifError(err)\n      })\n\n      clock.tick(100)\n      fastTimersTick(100)\n    })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/request-timeout2.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\n\ntest('request timeout with slow readable body', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n    let str = ''\n    for await (const x of req) {\n      str += x\n    }\n    res.end(str)\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`, { headersTimeout: 50 })\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  const body = new Readable({\n    read () {\n      if (this._reading) {\n        return\n      }\n      this._reading = true\n\n      this.push('asd')\n      setTimeout(() => {\n        this.push('asd')\n        this.push(null)\n      }, 2e3)\n    }\n  })\n  client.request({\n    path: '/',\n    method: 'POST',\n    headersTimeout: 1e3,\n    body\n  }, async (err, response) => {\n    t.ifError(err)\n    await response.body.dump()\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/request.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { createServer } = require('node:http')\nconst { test, after, describe } = require('node:test')\nconst { request, errors } = require('..')\n\ntest('no-slash/one-slash pathname should be included in req.path', async (t) => {\n  t = tspl(t, { plan: 24 })\n\n  const pathServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail('it shouldn\\'t be called')\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  const requestedServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(`/localhost:${pathServer.address().port}`, req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${requestedServer.address().port}`, req.headers.host)\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  after(() => {\n    requestedServer.close()\n    pathServer.close()\n  })\n\n  await Promise.all([\n    requestedServer.listen(0),\n    pathServer.listen(0)\n  ])\n\n  const noSlashPathname = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    pathname: `localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPathname.statusCode, 200)\n  const noSlashPath = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    path: `localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPath.statusCode, 200)\n  const noSlashPath2Arg = await request(\n    `http://localhost:${requestedServer.address().port}`,\n    { path: `localhost:${pathServer.address().port}` }\n  )\n  t.strictEqual(noSlashPath2Arg.statusCode, 200)\n  const oneSlashPathname = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    pathname: `/localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(oneSlashPathname.statusCode, 200)\n  const oneSlashPath = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    path: `/localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(oneSlashPath.statusCode, 200)\n  const oneSlashPath2Arg = await request(\n    `http://localhost:${requestedServer.address().port}`,\n    { path: `/localhost:${pathServer.address().port}` }\n  )\n  t.strictEqual(oneSlashPath2Arg.statusCode, 200)\n  t.end()\n})\n\ntest('protocol-relative URL as pathname should be included in req.path', async (t) => {\n  t = tspl(t, { plan: 12 })\n\n  const pathServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail('it shouldn\\'t be called')\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  const requestedServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(`//localhost:${pathServer.address().port}`, req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${requestedServer.address().port}`, req.headers.host)\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  after(() => {\n    requestedServer.close()\n    pathServer.close()\n  })\n\n  await Promise.all([\n    requestedServer.listen(0),\n    pathServer.listen(0)\n  ])\n\n  const noSlashPathname = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    pathname: `//localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPathname.statusCode, 200)\n  const noSlashPath = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    path: `//localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPath.statusCode, 200)\n  const noSlashPath2Arg = await request(\n    `http://localhost:${requestedServer.address().port}`,\n    { path: `//localhost:${pathServer.address().port}` }\n  )\n  t.strictEqual(noSlashPath2Arg.statusCode, 200)\n  t.end()\n})\n\ntest('Absolute URL as pathname should be included in req.path', async (t) => {\n  t = tspl(t, { plan: 12 })\n\n  const pathServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.fail('it shouldn\\'t be called')\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  const requestedServer = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.strictEqual(`/http://localhost:${pathServer.address().port}`, req.url)\n    t.strictEqual('GET', req.method)\n    t.strictEqual(`localhost:${requestedServer.address().port}`, req.headers.host)\n    res.statusCode = 200\n    res.end('hello')\n  })\n\n  after(() => {\n    requestedServer.close()\n    pathServer.close()\n  })\n\n  await Promise.all([\n    requestedServer.listen(0),\n    pathServer.listen(0)\n  ])\n\n  const noSlashPathname = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    pathname: `http://localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPathname.statusCode, 200)\n  const noSlashPath = await request({\n    method: 'GET',\n    origin: `http://localhost:${requestedServer.address().port}`,\n    path: `http://localhost:${pathServer.address().port}`\n  })\n  t.strictEqual(noSlashPath.statusCode, 200)\n  const noSlashPath2Arg = await request(\n    `http://localhost:${requestedServer.address().port}`,\n    { path: `http://localhost:${pathServer.address().port}` }\n  )\n  t.strictEqual(noSlashPath2Arg.statusCode, 200)\n  t.end()\n})\n\ndescribe('DispatchOptions#expectContinue', () => {\n  test('Should throw if invalid expectContinue option', async t => {\n    t = tspl(t, { plan: 1 })\n\n    await t.rejects(request({\n      method: 'GET',\n      origin: 'http://somehost.xyz',\n      expectContinue: 0\n    }), /invalid expectContinue/)\n\n    await t.completed\n  })\n})\n\ndescribe('DispatchOptions#maxRedirections', () => {\n  test('Should throw if maxRedirections option is used', async t => {\n    t = tspl(t, { plan: 2 })\n\n    await t.rejects(request({\n      method: 'GET',\n      origin: 'http://somehost.xyz',\n      maxRedirections: 5\n    }), /maxRedirections is not supported, use the redirect interceptor/)\n\n    await t.rejects(request({\n      method: 'GET',\n      origin: 'http://somehost.xyz',\n      maxRedirections: 1\n    }), /maxRedirections is not supported, use the redirect interceptor/)\n\n    await t.completed\n  })\n\n  test('Should allow maxRedirections: 0 for internal use', async t => {\n    t = tspl(t, { plan: 2 })\n    const server = createServer((req, res) => {\n      t.ok('request received')\n      res.end('hello world')\n    })\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n    await new Promise((resolve) => server.listen(0, resolve))\n\n    const res = await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      maxRedirections: 0\n    })\n\n    const body = await res.body.text()\n\n    t.strictEqual(body, 'hello world')\n  })\n})\n\ndescribe('DispatchOptions#reset', () => {\n  test('Should throw if invalid reset option', async t => {\n    t = tspl(t, { plan: 1 })\n\n    await t.rejects(request({\n      method: 'GET',\n      origin: 'http://somehost.xyz',\n      reset: 0\n    }), /invalid reset/)\n\n    await t.completed\n  })\n\n  test('Should include \"connection:close\" if reset true', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.connection, 'close')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: true\n    })\n  })\n\n  test('Should include \"connection:keep-alive\" if reset false', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.connection, 'keep-alive')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: false\n    })\n  })\n\n  test('Should react to manual set of \"connection:close\" header', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.connection, 'close')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      headers: {\n        connection: 'close'\n      }\n    })\n  })\n})\n\ndescribe('Should include headers from iterable objects', scope => {\n  test('Should include headers built with Headers global object', { skip: !globalThis.Headers }, async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.hello, 'world')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    const headers = new globalThis.Headers()\n    headers.set('hello', 'world')\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: true,\n      headers\n    })\n  })\n\n  test('Should include headers built with Map', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.hello, 'world')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    const headers = new Map()\n    headers.set('hello', 'world')\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: true,\n      headers\n    })\n  })\n\n  test('Should include headers built with custom iterable object', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.hello, 'world')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    const headers = {\n      * [Symbol.iterator] () {\n        yield ['hello', 'world']\n      }\n    }\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: true,\n      headers\n    })\n  })\n\n  test('Should include headers from plain objects with polluted Object.prototype[Symbol.iterator]', async t => {\n    t = tspl(t, { plan: 3 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.strictEqual('GET', req.method)\n      t.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n      t.strictEqual(req.headers.hello, 'world')\n      res.statusCode = 200\n      res.end('hello')\n    })\n\n    const headers = {\n      hello: 'world'\n    }\n\n    const originalIterator = Object.prototype[Symbol.iterator]\n    // eslint-disable-next-line no-extend-native\n    Object.prototype[Symbol.iterator] = function * () {}\n\n    try {\n      await new Promise((resolve, reject) => {\n        server.listen(0, (err) => {\n          if (err != null) reject(err)\n          else resolve()\n        })\n      })\n\n      await request({\n        method: 'GET',\n        origin: `http://localhost:${server.address().port}`,\n        reset: true,\n        headers\n      })\n    } finally {\n      if (originalIterator === undefined) {\n        delete Object.prototype[Symbol.iterator]\n      } else {\n        // eslint-disable-next-line no-extend-native\n        Object.prototype[Symbol.iterator] = originalIterator\n      }\n      server.closeAllConnections?.()\n      server.close()\n    }\n  })\n\n  test('Should throw error if headers iterable object does not yield key-value pairs', async t => {\n    t = tspl(t, { plan: 2 })\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.end('hello')\n    })\n\n    const headers = {\n      * [Symbol.iterator] () {\n        yield 'Bad formatted header'\n      }\n    }\n\n    after(() => {\n      server.closeAllConnections?.()\n      server.close()\n    })\n\n    await new Promise((resolve, reject) => {\n      server.listen(0, (err) => {\n        if (err != null) reject(err)\n        else resolve()\n      })\n    })\n\n    await request({\n      method: 'GET',\n      origin: `http://localhost:${server.address().port}`,\n      reset: true,\n      headers\n    }).catch((err) => {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'headers must be in key-value pair format')\n    })\n  })\n})\n\ntest('request should include statusText in response', async t => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer((req, res) => {\n    res.writeHead(200, 'Custom Status Text', { 'content-type': 'text/plain' })\n    res.end('hello')\n  })\n\n  after(() => {\n    server.closeAllConnections?.()\n    server.close()\n  })\n\n  await new Promise((resolve) => server.listen(0, resolve))\n\n  const { statusText, body } = await request({\n    method: 'GET',\n    origin: `http://localhost:${server.address().port}`,\n    path: '/'\n  })\n\n  t.strictEqual(statusText, 'Custom Status Text')\n  await body.dump()\n  t.ok('request completed')\n})\n"
  },
  {
    "path": "test/retry-agent.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\n\nconst { RetryAgent, Client } = require('..')\ntest('Should retry status code', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const opts = {\n    maxRetries: 5,\n    timeout: 1,\n    timeoutFactor: 1\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter++) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const agent = new RetryAgent(client, opts)\n\n    after(async () => {\n      await agent.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    agent.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      }\n    }).then((res) => {\n      t.equal(res.statusCode, 200)\n      res.body.setEncoding('utf8')\n      let chunks = ''\n      res.body.on('data', chunk => { chunks += chunk })\n      res.body.on('end', () => {\n        t.equal(chunks, 'hello world!')\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/retry-handler.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { Readable } = require('node:stream')\n\nconst { RetryHandler, Client } = require('..')\nconst { RequestHandler } = require('../lib/api/api-request')\n\ntest('Should retry status code', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        ++counter\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n          t.strictEqual(counter, 2)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should account for network and response errors', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter = state.counter\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n          t.strictEqual(counter, 2)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Issue #3288 - request with body (asynciterable)', async t => {\n  t = tspl(t, { plan: 4 })\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: (function * () {\n      yield 'hello'\n      yield 'world'\n    })()\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(500, {\n      'content-type': 'application/json'\n    })\n\n    res.end('{\"message\": \"failed\"}')\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          return true\n        },\n        onData (chunk) {\n          return true\n        },\n        onComplete () {\n          t.fail()\n        },\n        onError (err) {\n          t.equal(err.message, 'Request failed')\n          t.equal(err.statusCode, 500)\n          t.equal(err.data.count, 1)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(429, {\n          'retry-after': 1\n        })\n        res.end('rate limit')\n        checkpoint = Date.now()\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 500)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError (err) {\n          t.ifError(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries (date)', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        checkpoint = Date.now()\n        res.writeHead(429, {\n          'retry-after': new Date(\n            checkpoint + 2000\n          ).toUTCString()\n        })\n        res.end('rate limit')\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 1000)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError (err) {\n          t.ifError(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should retry with defaults', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        counter++\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        counter++\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        counter++\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError (err) {\n          t.ifError(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should handle 206 partial content', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, _resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should handle 206 partial content - bad-etag', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'erwsd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(\n      dispatchOptions,\n      {\n        dispatch: (...args) => {\n          return client.dispatch(...args)\n        },\n        handler: {\n          onConnect () {\n            t.ok(true, 'pass')\n          },\n          onHeaders (_status, _rawHeaders, _resume, _statusMessage) {\n            return true\n          },\n          onData (chunk) {\n            chunks.push(chunk)\n            return true\n          },\n          onComplete () {\n            t.ifError('should not complete')\n          },\n          onError (err) {\n            t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')\n            t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')\n            t.strictEqual(err.message, 'ETag mismatch')\n            t.deepEqual(err.data, { count: 2 })\n          }\n        }\n      }\n    )\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: JSON.stringify({ hello: 'world' })\n  }\n\n  t = tspl(t, { plan: 1 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'POST',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        },\n        body: JSON.stringify({ hello: 'world' })\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body (stream)', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: Readable.from(Buffer.from(JSON.stringify({ hello: 'world' })))\n  }\n\n  t = tspl(t, { plan: 3 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.equal(err.statusCode, 500)\n        t.equal(err.data.count, 1)\n        t.equal(err.code, 'UND_ERR_REQ_RETRY')\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body (buffer)', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: Buffer.from(JSON.stringify({ hello: 'world' }))\n  }\n\n  t = tspl(t, { plan: 1 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('should not error if request is not meant to be retried', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.writeHead(400)\n    res.end('Bad request')\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      }\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const chunks = []\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 400)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'Bad request')\n        },\n        onError (err) {\n          t.fail(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should be able to properly pass the minTimeout to the RetryContext when constructing a RetryCallback function', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: (err, { state, opts }, done) => {\n        counter++\n        t.strictEqual(opts.retryOptions.minTimeout, 100)\n\n        if (err.statusCode === 500) {\n          return done()\n        }\n\n        return done(err)\n      },\n      minTimeout: 100\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Issue#2986 - Handle custom 206', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Issue#3128 - Support if-match', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.deepStrictEqual(req.headers['if-match'], 'asd')\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Issue#3128 - Should ignore weak etags', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'W/asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.equal(req.headers['if-match'], undefined)\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'W/asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Weak etags are ignored on range-requests', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'W/asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.equal(req.headers['if-match'], undefined)\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'W/efg')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should throw RequestRetryError when Content-Range mismatch', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const chunks = []\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes bad') // intentionally bad to trigger error\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      retry: function (err, _, done) {\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, _resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.ifError('should not complete')\n        },\n        onError (err) {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')\n          t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')\n          t.strictEqual(err.message, 'Content-Range mismatch')\n          t.deepEqual(err.data, { count: 2 })\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries (date) but date format is wrong', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    retryOptions: {\n      minTimeout: 1000\n    }\n  }\n  const minRetryDelay = dispatchOptions.retryOptions.minTimeout\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0: {\n        checkpoint = process.hrtime.bigint()\n        res.writeHead(429, {\n          'retry-after': 'this is not a date'\n        })\n        res.end('rate limit')\n        counter++\n        return\n      }\n      case 1: {\n        res.writeHead(200)\n        res.end('hello world!')\n        const elapsedMs = Number(process.hrtime.bigint() - checkpoint) / 1e6\n        t.ok(elapsedMs >= minRetryDelay - 100)\n        counter++\n        return\n      }\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError (err) {\n          t.ifError(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/retry-handler2.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { once } = require('node:events')\nconst { Readable } = require('node:stream')\n\nconst { RetryHandler, Client, RetryAgent } = require('..')\nconst { RequestHandler } = require('../lib/api/api-request')\n\ntest('Reuses socket on retry instead of closing it', async t => {\n  t = tspl(t, { plan: 5 })\n  let counter = 0\n  let socketPort\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    counter++\n    res.writeHead(500)\n    res.end('internal err')\n\n    if (!socketPort) {\n      socketPort = req.socket.remotePort\n    }\n\n    t.strictEqual(socketPort, req.socket.remotePort)\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const retryAgent = new RetryAgent(client, {\n      throwOnError: false,\n      maxRetries: 2\n    })\n\n    retryAgent.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      }\n    }).then(res => {\n      t.strictEqual(res.statusCode, 500)\n      t.strictEqual(counter, 3)\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('throws an error on network error', async t => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.destroy()\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const retryAgent = new RetryAgent(client, {\n      throwOnError: false,\n      maxRetries: 2\n    })\n\n    retryAgent.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      }\n    }).catch(err => {\n      t.strictEqual(err.code, 'UND_ERR_SOCKET')\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Show pass status code errors when not eligible for retry, as normal response instead of throwing error', async t => {\n  t = tspl(t, { plan: 3 })\n  let counter = 0\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    counter++\n    res.writeHead(500)\n    res.end('internal err')\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const retryAgent = new RetryAgent(client, {\n      throwOnError: false,\n      maxRetries: 2\n    })\n\n    retryAgent.request({\n      method: 'GET',\n      path: '/',\n      headers: {\n        'content-type': 'application/json'\n      }\n    }).then(res => {\n      t.strictEqual(res.statusCode, 500)\n      t.strictEqual(counter, 3)\n      res.body.text().then(text => {\n        t.strictEqual(text, 'internal err')\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should retry status code without throwing an error | throwOnError: false', async t => {\n  t = tspl(t, { plan: 6 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        ++counter\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n          t.strictEqual(counter, 2)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should account for network and response errors | throwOnError: false', async t => {\n  t = tspl(t, { plan: 6 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        counter = state.counter\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n          t.strictEqual(counter, 2)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Issue #3288 - request with body (asynciterable) should fail, without throwing an error', async t => {\n  t = tspl(t, { plan: 3 })\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: (function * () {\n      yield 'hello'\n      yield 'world'\n    })()\n  }\n\n  server.on('request', (req, res) => {\n    res.writeHead(500, {\n      'content-type': 'application/json'\n    })\n\n    res.end('{\"message\": \"failed\"}')\n  })\n\n  const chunks = []\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.ok(true, 'pass')\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          const data = Buffer.concat(chunks).toString('utf-8')\n          t.strictEqual(data, '{\"message\": \"failed\"}')\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries | throwOnError: false', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(429, {\n          'retry-after': 1\n        })\n        res.end('rate limit')\n        checkpoint = Date.now()\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 500)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries (date) | throwOnError: false', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        checkpoint = Date.now()\n        res.writeHead(429, {\n          'retry-after': new Date(\n            checkpoint + 2000\n          ).toUTCString()\n        })\n        res.end('rate limit')\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 1000)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should retry with defaults | throwOnError: false', async t => {\n  t = tspl(t, { plan: 5 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        counter++\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        counter++\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        counter++\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should handle 206 partial content | throwOnError: false', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, _resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should handle 206 partial content - bad-etag | throwOnError: false', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'erwsd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(\n      dispatchOptions,\n      {\n        dispatch: (...args) => {\n          return client.dispatch(...args)\n        },\n        handler: {\n          onConnect () {\n            t.ok(true, 'pass')\n          },\n          onHeaders (_status, _rawHeaders, _resume, _statusMessage) {\n            return true\n          },\n          onData (chunk) {\n            chunks.push(chunk)\n            return true\n          },\n          onComplete () {\n            t.ifError('should not complete')\n          },\n          onError (err) {\n            t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')\n            t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')\n            t.strictEqual(err.message, 'ETag mismatch')\n            t.deepEqual(err.data, { count: 2 })\n          }\n        }\n      }\n    )\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body | throwOnError: false', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: JSON.stringify({ hello: 'world' })\n  }\n\n  t = tspl(t, { plan: 1 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'POST',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        },\n        body: JSON.stringify({ hello: 'world' })\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body (stream) | throwOnError: false', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: Readable.from(Buffer.from(JSON.stringify({ hello: 'world' })))\n  }\n\n  t = tspl(t, { plan: 3 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n        t.equal(data.statusCode, 500)\n        data.body.text().then(text => {\n          t.equal(text, 'failed')\n        })\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('retrying a request with a body (buffer) | throwOnError: false', async t => {\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        counter++\n\n        if (\n          err.statusCode === 500 ||\n          err.message.includes('other side closed')\n        ) {\n          setTimeout(done, 500)\n          return\n        }\n\n        return done(err)\n      }\n    },\n    method: 'POST',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: Buffer.from(JSON.stringify({ hello: 'world' }))\n  }\n\n  t = tspl(t, { plan: 1 })\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        req.destroy()\n        return\n      case 1:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 2:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      dispatchOptions,\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('should not error if request is not meant to be retried | throwOnError: false', async t => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    res.writeHead(400)\n    res.end('Bad request')\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const chunks = []\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 400)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'Bad request')\n        },\n        onError (err) {\n          t.fail(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Should be able to properly pass the minTimeout to the RetryContext when constructing a RetryCallback function | throwOnError: false', async t => {\n  t = tspl(t, { plan: 2 })\n\n  let counter = 0\n  const server = createServer({ joinDuplicateHeaders: true })\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        res.writeHead(500)\n        res.end('failed')\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        return\n      default:\n        t.fail()\n    }\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: (err, { state, opts }, done) => {\n        counter++\n        t.strictEqual(opts.retryOptions.minTimeout, 100)\n\n        if (err.statusCode === 500) {\n          return done()\n        }\n\n        return done(err)\n      },\n      minTimeout: 100\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: new RequestHandler(dispatchOptions, (err, data) => {\n        t.ifError(err)\n      })\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n\ntest('Issue#2986 - Handle custom 206 | throwOnError: false', async t => {\n  t = tspl(t, { plan: 6 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Issue#3128 - Support if-match | throwOnError: false', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.deepStrictEqual(req.headers['if-match'], 'asd')\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Issue#3128 - Should ignore weak etags | throwOnError: false', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'W/asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.equal(req.headers['if-match'], undefined)\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'W/asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Weak etags are ignored on range-requests | throwOnError: false', async t => {\n  t = tspl(t, { plan: 7 })\n\n  const chunks = []\n  let counter = 0\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.deepStrictEqual(req.headers.range, 'bytes=0-3')\n      res.setHeader('etag', 'W/asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      t.equal(req.headers['if-match'], undefined)\n\n      res.setHeader('content-range', 'bytes 3-6/6')\n      res.setHeader('etag', 'W/efg')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        counter++\n\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abcdef')\n          t.strictEqual(counter, 1)\n        },\n        onError () {\n          t.fail()\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json',\n          Range: 'bytes=0-3'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should throw RequestRetryError when Content-Range mismatch | throwOnError: false', async t => {\n  t = tspl(t, { plan: 8 })\n\n  const chunks = []\n\n  // Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47\n  let x = 0\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    if (x === 0) {\n      t.ok(true, 'pass')\n      res.setHeader('etag', 'asd')\n      res.write('abc')\n      setTimeout(() => {\n        res.destroy()\n      }, 1e2)\n    } else if (x === 1) {\n      t.deepStrictEqual(req.headers.range, 'bytes=3-')\n      res.setHeader('content-range', 'bytes bad') // intentionally bad to trigger error\n      res.setHeader('etag', 'asd')\n      res.statusCode = 206\n      res.end('def')\n    }\n    x++\n  })\n\n  const dispatchOptions = {\n    retryOptions: {\n      throwOnError: false,\n      retry: function (err, _, done) {\n        if (err.code && err.code === 'UND_ERR_DESTROYED') {\n          return done(false)\n        }\n\n        if (err.statusCode === 206) return done(err)\n\n        setTimeout(done, 800)\n      }\n    },\n    method: 'GET',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    }\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: (...args) => {\n        return client.dispatch(...args)\n      },\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, _resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.ifError('should not complete')\n        },\n        onError (err) {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')\n          t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')\n          t.strictEqual(err.message, 'Content-Range mismatch')\n          t.deepEqual(err.data, { count: 2 })\n        }\n      }\n    })\n\n    client.dispatch(\n      {\n        method: 'GET',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n\n    after(async () => {\n      await client.close()\n\n      server.close()\n      await once(server, 'close')\n    })\n  })\n\n  await t.completed\n})\n\ntest('Should use retry-after header for retries (date) but date format is wrong | throwOnError: false', async t => {\n  t = tspl(t, { plan: 3 })\n\n  let counter = 0\n  const chunks = []\n  const server = createServer({ joinDuplicateHeaders: true })\n  let checkpoint\n  const dispatchOptions = {\n    method: 'PUT',\n    path: '/',\n    headers: {\n      'content-type': 'application/json'\n    },\n    retryOptions: {\n      minTimeout: 1000,\n      throwOnError: false\n    }\n  }\n\n  server.on('request', (req, res) => {\n    switch (counter) {\n      case 0:\n        checkpoint = Date.now()\n        res.writeHead(429, {\n          'retry-after': 'this is not a date'\n        })\n        res.end('rate limit')\n        counter++\n        return\n      case 1:\n        res.writeHead(200)\n        res.end('hello world!')\n        t.ok(Date.now() - checkpoint >= 1000)\n        counter++\n        return\n      default:\n        t.fail('unexpected request')\n    }\n  })\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    const handler = new RetryHandler(dispatchOptions, {\n      dispatch: client.dispatch.bind(client),\n      handler: {\n        onConnect () {\n          t.ok(true, 'pass')\n        },\n        onHeaders (status, _rawHeaders, resume, _statusMessage) {\n          t.strictEqual(status, 200)\n          return true\n        },\n        onData (chunk) {\n          chunks.push(chunk)\n          return true\n        },\n        onComplete () {\n          t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'hello world!')\n        },\n        onError (err) {\n          t.ifError(err)\n        }\n      }\n    })\n\n    after(async () => {\n      await client.close()\n      server.close()\n\n      await once(server, 'close')\n    })\n\n    client.dispatch(\n      {\n        method: 'PUT',\n        path: '/',\n        headers: {\n          'content-type': 'application/json'\n        }\n      },\n      handler\n    )\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/round-robin-pool.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst {\n  RoundRobinPool,\n  Client,\n  errors\n} = require('..')\n\ntest('throws when connection is infinite', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new RoundRobinPool(null, { connections: 0 / 0 }) // eslint-disable-line\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid connections')\n  }\n})\n\ntest('throws when connections is negative', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  try {\n    new RoundRobinPool(null, { connections: -1 }) // eslint-disable-line\n  } catch (e) {\n    t.ok(e instanceof errors.InvalidArgumentError)\n    t.strictEqual(e.message, 'invalid connections')\n  }\n})\n\ntest('throws when factory is not a function', (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  try {\n    new RoundRobinPool('http://localhost', { factory: '' }) // eslint-disable-line\n  } catch (err) {\n    p.ok(err instanceof errors.InvalidArgumentError)\n    p.strictEqual(err.message, 'factory must be a function.')\n  }\n})\n\ntest('passes socketPath to custom connect function', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const connectError = new Error('custom connect error')\n  const socketPath = '/var/run/test.sock'\n  const pool = new RoundRobinPool('http://localhost', {\n    socketPath,\n    connect (opts, cb) {\n      p.strictEqual(opts.socketPath, socketPath)\n      cb(connectError, null)\n    }\n  })\n  t.after(() => pool.close())\n\n  pool.request({\n    path: '/',\n    method: 'GET'\n  }, (err) => {\n    p.strictEqual(err, connectError)\n  })\n\n  await p.completed\n})\n\ntest('basic get', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer((req, res) => {\n    t.strictEqual('/', req.url)\n    t.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n\n  after(() => server.close())\n\n  await new Promise(resolve => server.listen(0, resolve))\n\n  const pool = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n    connections: 1\n  })\n\n  after(() => pool.close())\n\n  const { statusCode, body } = await pool.request({ path: '/', method: 'GET' })\n  t.strictEqual(statusCode, 200)\n\n  const text = await body.text()\n  t.strictEqual(text, 'hello')\n\n  await t.completed\n})\n\ntest('connect/disconnect event(s)', async (t) => {\n  const clients = 2\n\n  const p = tspl(t, { plan: clients * 5 })\n\n  const server = createServer((req, res) => {\n    res.writeHead(200, {\n      Connection: 'keep-alive',\n      'Keep-Alive': 'timeout=1s'\n    })\n    res.end('ok')\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, () => {\n    const pool = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n      connections: clients,\n      keepAliveTimeoutThreshold: 100\n    })\n    t.after(() => pool.close())\n\n    pool.on('connect', (origin, [pool, client]) => {\n      p.ok(client instanceof Client)\n    })\n    pool.on('disconnect', (origin, [pool, client], error) => {\n      p.ok(client instanceof Client)\n      p.ok(error instanceof errors.InformationalError)\n      p.strictEqual(error.code, 'UND_ERR_INFO')\n    })\n\n    for (let i = 0; i < clients; i++) {\n      pool.request({\n        path: '/',\n        method: 'GET'\n      }, (err, { body }) => {\n        p.ifError(err)\n        body.resume()\n      })\n    }\n  })\n\n  await p.completed\n})\n\ntest('busy', async (t) => {\n  const p = tspl(t, { plan: 8 * 6 + 2 + 1 })\n\n  const server = createServer((req, res) => {\n    p.strictEqual('/', req.url)\n    p.strictEqual('GET', req.method)\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server.close.bind(server))\n\n  server.listen(0, async () => {\n    const client = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n      connections: 2,\n      pipelining: 2\n    })\n    client.on('drain', () => {\n      p.ok(1)\n    })\n    client.on('connect', () => {\n      p.ok(1)\n    })\n    t.after(client.destroy.bind(client))\n\n    for (let n = 1; n <= 8; ++n) {\n      client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => {\n        p.ifError(err)\n        p.strictEqual(statusCode, 200)\n        p.strictEqual(headers['content-type'], 'text/plain')\n        const bufs = []\n        body.on('data', (buf) => {\n          bufs.push(buf)\n        })\n        body.on('end', () => {\n          p.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        })\n      })\n    }\n  })\n\n  await p.completed\n})\n\ntest('factory option with basic get request', async (t) => {\n  const p = tspl(t, { plan: 8 })\n\n  let factoryCalled = 0\n  const opts = {\n    connections: 1,\n    factory: (origin, opts) => {\n      factoryCalled++\n      return new Client(origin, opts)\n    }\n  }\n\n  const server = createServer((req, res) => {\n    res.setHeader('content-type', 'text/plain')\n    res.end('hello')\n  })\n  t.after(server.close.bind(server))\n\n  await promisify(server.listen).call(server, 0)\n\n  const client = new RoundRobinPool(`http://localhost:${server.address().port}`, opts)\n\n  t.after(client.destroy.bind(client))\n\n  const { statusCode, headers, body } = await client.request({ path: '/', method: 'GET' })\n  p.strictEqual(statusCode, 200)\n  p.strictEqual(headers['content-type'], 'text/plain')\n  p.strictEqual('hello', await body.text())\n\n  p.ok(factoryCalled >= 1) // May create one or more clients\n\n  p.strictEqual(client.destroyed, false)\n  p.strictEqual(client.closed, false)\n  await client.close()\n  p.strictEqual(client.destroyed, true)\n  p.strictEqual(client.closed, true)\n})\n\ntest('round-robin distribution with multiple requests', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  let totalRequests = 0\n  const clientRequests = new Map() // Track requests per client connection\n\n  const server = createServer((req, res) => {\n    totalRequests++\n    // Track which connection this request came from via socket remote port\n    const clientKey = `${req.socket.remoteAddress}:${req.socket.remotePort}`\n    clientRequests.set(clientKey, (clientRequests.get(clientKey) || 0) + 1)\n\n    // Add delay to make clients busy and force creation of multiple connections\n    setTimeout(() => {\n      res.writeHead(200, { 'Content-Type': 'text/plain' })\n      res.end('ok')\n    }, 50)\n  })\n\n  after(() => server.close())\n\n  await new Promise(resolve => server.listen(0, resolve))\n\n  const pool = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n    connections: 3\n  })\n\n  after(() => pool.close())\n\n  // This forces creation of multiple connections\n  const requests = []\n  for (let i = 0; i < 30; i++) {\n    requests.push(pool.request({ path: '/', method: 'GET' }).then(({ body }) => body.text()))\n  }\n  await Promise.all(requests)\n\n  p.strictEqual(totalRequests, 30)\n\n  // Check that multiple connections were used (not all requests on one connection)\n  // With round-robin, we should have close to equal distribution\n  const requestCounts = Array.from(clientRequests.values())\n  const max = Math.max(...requestCounts)\n  const min = Math.min(...requestCounts)\n  const ratio = max / min\n\n  // With round-robin and concurrent requests forcing multiple connections:\n  // should see relatively even distribution (ratio < 2.5)\n  p.ok(ratio < 2.5, `Distribution ratio ${ratio.toFixed(2)}x should be < 2.5 (counts: ${requestCounts.join(', ')})`)\n\n  await p.completed\n})\n\ntest('round-robin wraps around correctly', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n    res.writeHead(200)\n    res.end('ok')\n  })\n\n  after(() => server.close())\n  await new Promise(resolve => server.listen(0, resolve))\n\n  const pool = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n    connections: 2\n  })\n\n  after(() => pool.close())\n\n  // Make more requests than connections to ensure wrapping\n  for (let i = 0; i < 5; i++) {\n    const { body } = await pool.request({ path: '/', method: 'GET' })\n    await body.text()\n  }\n\n  t.strictEqual(requestCount, 5)\n  t.ok(pool.stats.connected <= 2)\n\n  await t.completed\n})\n\ntest('close/destroy behavior', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer((req, res) => {\n    res.end('ok')\n  })\n\n  after(() => server.close())\n  await new Promise(resolve => server.listen(0, resolve))\n\n  const pool = new RoundRobinPool(`http://localhost:${server.address().port}`)\n\n  t.strictEqual(pool.destroyed, false)\n  t.strictEqual(pool.closed, false)\n\n  await pool.close()\n\n  t.strictEqual(pool.destroyed, true)\n  t.strictEqual(pool.closed, true)\n\n  await t.completed\n})\n\ntest('verifies round-robin kGetDispatcher cycling algorithm', async (t) => {\n  t = tspl(t, { plan: 4 })\n\n  const server = createServer((req, res) => {\n    res.end('ok')\n  })\n\n  after(() => server.close())\n  await new Promise(resolve => server.listen(0, resolve))\n\n  const clientOrder = []\n  let clientIdCounter = 0\n\n  const pool = new RoundRobinPool(`http://localhost:${server.address().port}`, {\n    connections: 3,\n    factory: (origin, opts) => {\n      const client = new Client(origin, opts)\n      const id = clientIdCounter++\n\n      // Intercept dispatch to track which client handles each request\n      const originalDispatch = client.dispatch.bind(client)\n      client.dispatch = function (opts, handler) {\n        clientOrder.push(id)\n        return originalDispatch(opts, handler)\n      }\n\n      return client\n    }\n  })\n\n  after(() => pool.close())\n\n  // Make 6 requests concurrently\n  const responses = await Promise.all([\n    pool.request({ path: '/', method: 'GET' }),\n    pool.request({ path: '/', method: 'GET' }),\n    pool.request({ path: '/', method: 'GET' }),\n    pool.request({ path: '/', method: 'GET' }),\n    pool.request({ path: '/', method: 'GET' }),\n    pool.request({ path: '/', method: 'GET' })\n  ])\n\n  await Promise.all(responses.map(({ body }) => body.text()))\n\n  // Verify core round-robin behavior\n  t.strictEqual(clientIdCounter, 3, 'Should create exactly 3 clients')\n  t.deepStrictEqual(clientOrder.slice(0, 3), [0, 1, 2], 'First 3 dispatches create clients 0,1,2 in order')\n  t.ok(clientOrder.every(id => id < 3), 'All dispatches use one of the 3 clients')\n\n  // Verify all clients were used (proves cycling)\n  const uniqueClients = new Set(clientOrder)\n  t.strictEqual(uniqueClients.size, 3, 'All 3 clients used (cycling verified)')\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/snapshot-recorder.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { tmpdir } = require('node:os')\nconst { join } = require('node:path')\nconst { unlink } = require('node:fs/promises')\nconst { SnapshotRecorder, formatRequestKey, createRequestHash, filterHeadersForMatching, filterHeadersForStorage, createHeaderFilters } = require('../lib/mock/snapshot-recorder')\n\ntest('SnapshotRecorder - basic recording and retrieval', (t) => {\n  const recorder = new SnapshotRecorder()\n\n  const requestOpts = {\n    origin: 'https://api.example.com',\n    path: '/users/123',\n    method: 'GET',\n    headers: { authorization: 'Bearer token' }\n  }\n\n  const response = {\n    statusCode: 200,\n    headers: { 'content-type': 'application/json' },\n    body: Buffer.from('{\"id\": 123, \"name\": \"Test User\"}'),\n    trailers: {}\n  }\n\n  // Record the interaction\n  recorder.record(requestOpts, response)\n\n  // Verify it was recorded\n  assert.strictEqual(recorder.size(), 1)\n\n  // Retrieve the snapshot\n  const snapshot = recorder.findSnapshot(requestOpts)\n  assert(snapshot)\n  assert.strictEqual(snapshot.request.method, 'GET')\n  assert.strictEqual(snapshot.request.url, 'https://api.example.com/users/123')\n  assert.strictEqual(snapshot.response.statusCode, 200)\n  // Body is stored as base64 string\n  assert.strictEqual(snapshot.response.body, response.body.toString('base64'))\n})\n\ntest('SnapshotRecorder - request key formatting', (t) => {\n  const requestOpts = {\n    origin: 'https://api.example.com',\n    path: '/search?q=test&limit=10',\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: 'Bearer token'\n    },\n    body: '{\"filter\": \"active\"}'\n  }\n\n  const cachedSets = createHeaderFilters({})\n  const formatted = formatRequestKey(requestOpts, cachedSets)\n\n  assert.strictEqual(formatted.method, 'POST')\n  assert.strictEqual(formatted.url, 'https://api.example.com/search?q=test&limit=10')\n  assert.strictEqual(formatted.headers['content-type'], 'application/json')\n  assert.strictEqual(formatted.headers.authorization, 'Bearer token')\n  assert.strictEqual(formatted.body, '{\"filter\": \"active\"}')\n})\n\ntest('SnapshotRecorder - request hashing', (t) => {\n  const request1 = {\n    method: 'GET',\n    url: 'https://api.example.com/users',\n    headers: { authorization: 'Bearer token' },\n    body: undefined\n  }\n\n  const request2 = {\n    method: 'GET',\n    url: 'https://api.example.com/users',\n    headers: { authorization: 'Bearer token' },\n    body: undefined\n  }\n\n  const request3 = {\n    method: 'POST',\n    url: 'https://api.example.com/users',\n    headers: { authorization: 'Bearer token' },\n    body: undefined\n  }\n\n  const hash1 = createRequestHash(request1)\n  const hash2 = createRequestHash(request2)\n  const hash3 = createRequestHash(request3)\n\n  // Same requests should have same hash\n  assert.strictEqual(hash1, hash2)\n\n  // Different requests should have different hashes\n  assert.notStrictEqual(hash1, hash3)\n\n  // Hashes should be URL-safe base64\n  assert(hash1.match(/^[A-Za-z0-9_-]+$/))\n})\n\ntest('SnapshotRecorder - header normalization', (t) => {\n  const requestOpts1 = {\n    origin: 'https://api.example.com',\n    path: '/test',\n    headers: {\n      'Content-Type': 'application/json',\n      AUTHORIZATION: 'Bearer token'\n    }\n  }\n\n  const requestOpts2 = {\n    origin: 'https://api.example.com',\n    path: '/test',\n    headers: {\n      'content-type': 'application/json',\n      authorization: 'Bearer token'\n    }\n  }\n\n  const cachedSets = createHeaderFilters({})\n  const formatted1 = formatRequestKey(requestOpts1, cachedSets)\n  const formatted2 = formatRequestKey(requestOpts2, cachedSets)\n\n  // Headers should be normalized to lowercase\n  assert.deepStrictEqual(formatted1.headers, formatted2.headers)\n  assert.strictEqual(formatted1.headers['content-type'], 'application/json')\n  assert.strictEqual(formatted1.headers.authorization, 'Bearer token')\n})\n\ntest('SnapshotRecorder - file persistence', async (t) => {\n  const snapshotPath = join(tmpdir(), `test-recorder-${Date.now()}.json`)\n  const recorder = new SnapshotRecorder({ snapshotPath })\n\n  t.after(() => unlink(snapshotPath).catch(() => {}))\n\n  // Record some interactions\n  await recorder.record(\n    { origin: 'https://api.example.com', path: '/users', method: 'GET' },\n    { statusCode: 200, headers: {}, body: Buffer.from('user data'), trailers: {} }\n  )\n\n  await recorder.record(\n    { origin: 'https://api.example.com', path: '/posts', method: 'GET' },\n    { statusCode: 200, headers: {}, body: Buffer.from('post data'), trailers: {} }\n  )\n\n  assert.strictEqual(recorder.size(), 2)\n\n  // Save to file\n  await recorder.saveSnapshots()\n\n  // Create new recorder and load from file\n  const newRecorder = new SnapshotRecorder({ snapshotPath })\n  await newRecorder.loadSnapshots()\n\n  assert.strictEqual(newRecorder.size(), 2)\n\n  // Verify snapshots were loaded correctly\n  const userSnapshot = newRecorder.findSnapshot({\n    origin: 'https://api.example.com',\n    path: '/users',\n    method: 'GET'\n  })\n\n  assert(userSnapshot)\n  assert.strictEqual(userSnapshot.response.statusCode, 200)\n  // Body is now stored as base64 string\n  assert.strictEqual(userSnapshot.response.body, Buffer.from('user data').toString('base64'))\n})\n\ntest('SnapshotRecorder - loading non-existent file', async (t) => {\n  const snapshotPath = join(tmpdir(), `non-existent-${Date.now()}.json`)\n  const recorder = new SnapshotRecorder({ snapshotPath })\n\n  // Should not throw, just create empty recorder\n  await recorder.loadSnapshots()\n  assert.strictEqual(recorder.size(), 0)\n})\n\ntest('SnapshotRecorder - array header handling', (t) => {\n  const requestOpts = {\n    origin: 'https://api.example.com',\n    path: '/test',\n    headers: {\n      accept: ['application/json', 'text/plain'],\n      'x-custom': 'single-value'\n    }\n  }\n\n  const cachedSets = createHeaderFilters({})\n  const formatted = formatRequestKey(requestOpts, cachedSets)\n\n  // Array headers should be joined with comma\n  assert.strictEqual(formatted.headers.accept, 'application/json, text/plain')\n  assert.strictEqual(formatted.headers['x-custom'], 'single-value')\n})\n\ntest('SnapshotRecorder - query parameter handling', (t) => {\n  const requestOpts1 = {\n    origin: 'https://api.example.com',\n    path: '/search?q=test&sort=date',\n    method: 'GET'\n  }\n\n  const requestOpts2 = {\n    origin: 'https://api.example.com',\n    path: '/search?sort=date&q=test', // Different order\n    method: 'GET'\n  }\n\n  const cachedSets = createHeaderFilters({})\n  const formatted1 = formatRequestKey(requestOpts1, cachedSets)\n  const formatted2 = formatRequestKey(requestOpts2, cachedSets)\n\n  // URLs with different query parameter order should be normalized\n  assert.strictEqual(formatted1.url, 'https://api.example.com/search?q=test&sort=date')\n\n  // But they should still create different hashes if params are truly different\n  const hash1 = createRequestHash(formatted1)\n  const hash2 = createRequestHash(formatted2)\n\n  // This tests that parameter order matters in our current implementation\n  // We might want to normalize parameter order in the future\n  assert.notStrictEqual(hash1, hash2)\n})\n\ntest('SnapshotRecorder - clear functionality', async (t) => {\n  const recorder = new SnapshotRecorder()\n\n  // Record some snapshots\n  await recorder.record(\n    { origin: 'https://api.example.com', path: '/test1' },\n    { statusCode: 200, headers: {}, body: Buffer.from('data1'), trailers: {} }\n  )\n\n  await recorder.record(\n    { origin: 'https://api.example.com', path: '/test2' },\n    { statusCode: 200, headers: {}, body: Buffer.from('data2'), trailers: {} }\n  )\n\n  assert.strictEqual(recorder.size(), 2)\n\n  // Clear and verify\n  recorder.clear()\n  assert.strictEqual(recorder.size(), 0)\n\n  // Should not find any snapshots\n  const snapshot = recorder.findSnapshot({\n    origin: 'https://api.example.com',\n    path: '/test1'\n  })\n  assert.strictEqual(snapshot, undefined)\n})\n\ntest('SnapshotRecorder - custom header matching', (t) => {\n  const headers = {\n    'content-type': 'application/json',\n    authorization: 'Bearer token',\n    'x-request-id': '123',\n    accept: 'application/json'\n  }\n\n  // Test matchHeaders option\n  const matchSpecificOptions = { matchHeaders: ['content-type', 'accept'] }\n  const matchSpecificCachedSets = createHeaderFilters(matchSpecificOptions)\n  const matchSpecific = filterHeadersForMatching(headers, matchSpecificCachedSets, matchSpecificOptions)\n\n  assert.deepStrictEqual(matchSpecific, {\n    'content-type': 'application/json',\n    accept: 'application/json'\n  })\n\n  // Test ignoreHeaders option\n  const ignoreOptions = { ignoreHeaders: ['authorization', 'x-request-id'] }\n  const ignoreCachedSets = createHeaderFilters(ignoreOptions)\n  const ignoreAuth = filterHeadersForMatching(headers, ignoreCachedSets, ignoreOptions)\n\n  assert.deepStrictEqual(ignoreAuth, {\n    'content-type': 'application/json',\n    accept: 'application/json'\n  })\n\n  // Test excludeHeaders option\n  const excludeOptions = { excludeHeaders: ['authorization'] }\n  const excludeCachedSets = createHeaderFilters(excludeOptions)\n  const excludeSensitive = filterHeadersForMatching(headers, excludeCachedSets, excludeOptions)\n\n  assert.deepStrictEqual(excludeSensitive, {\n    'content-type': 'application/json',\n    'x-request-id': '123',\n    accept: 'application/json'\n  })\n})\n\ntest('SnapshotRecorder - header filtering for storage', (t) => {\n  const headers = {\n    'content-type': 'application/json',\n    'set-cookie': 'session=secret',\n    authorization: 'Bearer token',\n    'cache-control': 'no-cache'\n  }\n\n  // Test excluding sensitive headers from storage\n  const filtered = filterHeadersForStorage(headers, {\n    exclude: new Set(['set-cookie', 'authorization'])\n  })\n\n  assert.deepStrictEqual(filtered, {\n    'content-type': 'application/json',\n    'cache-control': 'no-cache'\n  })\n})\n\ntest('SnapshotRecorder - case sensitivity in header filtering', (t) => {\n  const headers = {\n    'Content-Type': 'application/json',\n    AUTHORIZATION: 'Bearer token',\n    'X-Request-ID': '123'\n  }\n\n  // Test case insensitive (default)\n  const caseInsensitiveOptions = { ignoreHeaders: ['authorization', 'x-request-id'] }\n  const caseInsensitiveCachedSets = createHeaderFilters(caseInsensitiveOptions)\n  const caseInsensitive = filterHeadersForMatching(headers, caseInsensitiveCachedSets, caseInsensitiveOptions)\n\n  assert.deepStrictEqual(caseInsensitive, {\n    'content-type': 'application/json'\n  })\n\n  // Test case sensitive\n  const caseSensitiveOptions = { ignoreHeaders: ['authorization', 'x-request-id'], caseSensitive: true }\n  const caseSensitiveCachedSets = createHeaderFilters(caseSensitiveOptions)\n  const caseSensitive = filterHeadersForMatching(headers, caseSensitiveCachedSets, caseSensitiveOptions)\n\n  // Should keep all headers since case doesn't match\n  assert.deepStrictEqual(caseSensitive, {\n    'Content-Type': 'application/json',\n    AUTHORIZATION: 'Bearer token',\n    'X-Request-ID': '123'\n  })\n})\n\ntest('SnapshotRecorder - request formatting with match options', (t) => {\n  const requestOpts = {\n    origin: 'https://api.example.com',\n    path: '/search?q=test&limit=10',\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: 'Bearer token',\n      'X-Request-ID': '123'\n    },\n    body: '{\"filter\": \"active\"}'\n  }\n\n  // Test with matchHeaders option\n  const matchOptions = {\n    matchHeaders: ['content-type'],\n    matchBody: false,\n    matchQuery: false\n  }\n  const cachedSets = createHeaderFilters(matchOptions)\n  const formatted = formatRequestKey(requestOpts, cachedSets, matchOptions)\n\n  assert.strictEqual(formatted.method, 'POST')\n  assert.strictEqual(formatted.url, 'https://api.example.com/search') // No query\n  assert.deepStrictEqual(formatted.headers, {\n    'content-type': 'application/json'\n  })\n  assert.strictEqual(formatted.body, '') // No body\n})\n\ntest('SnapshotRecorder - redirect responses are stored correctly', (t) => {\n  const recorder = new SnapshotRecorder()\n\n  // Initial request to the redirect URL\n  const redirectRequestOpts = {\n    origin: 'https://api.example.com',\n    path: '/redirect-start',\n    method: 'GET',\n    headers: { accept: 'application/json' }\n  }\n\n  // First response: 302 redirect (this should not be stored)\n  const redirectResponse = {\n    statusCode: 302,\n    headers: { location: '/redirect-target' },\n    body: Buffer.from('Redirecting...'),\n    trailers: {}\n  }\n\n  // Final response: 200 success (this should be stored)\n  const finalResponse = {\n    statusCode: 200,\n    headers: { 'content-type': 'application/json' },\n    body: Buffer.from('{\"message\": \"Final destination\"}'),\n    trailers: {}\n  }\n\n  // Record the redirect response (this will be stored as it's a valid response)\n  recorder.record(redirectRequestOpts, redirectResponse)\n  assert.strictEqual(recorder.size(), 1, 'Redirect response (302) should be stored')\n\n  // First snapshot should contain the redirect response\n  let snapshot = recorder.findSnapshot(redirectRequestOpts)\n  assert(snapshot, 'Should find snapshot for redirect request')\n  assert.strictEqual(snapshot.request.url, 'https://api.example.com/redirect-start')\n  assert.strictEqual(snapshot.response.statusCode, 302, 'First stored response should be the 302 redirect')\n\n  // Record the final response (this will create a second response for the same request)\n  recorder.record(redirectRequestOpts, finalResponse)\n  assert.strictEqual(recorder.size(), 1, 'Should still have one snapshot (same request)')\n\n  // Retrieve the snapshot again - should now have multiple responses\n  snapshot = recorder.findSnapshot(redirectRequestOpts)\n  assert(snapshot, 'Should find snapshot for redirect request')\n  assert.strictEqual(snapshot.request.url, 'https://api.example.com/redirect-start')\n\n  // The recorder supports sequential responses, so it should have both\n  assert(Array.isArray(snapshot.responses), 'Should have responses array')\n  assert.strictEqual(snapshot.responses.length, 2, 'Should have two responses')\n  assert.strictEqual(snapshot.responses[0].statusCode, 302, 'First response should be redirect')\n  assert.strictEqual(snapshot.responses[1].statusCode, 200, 'Second response should be final')\n})\n"
  },
  {
    "path": "test/snapshot-redirect-interceptor.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { unlink } = require('node:fs/promises')\nconst { tmpdir } = require('node:os')\nconst { join } = require('node:path')\nconst { SnapshotAgent, setGlobalDispatcher, getGlobalDispatcher, request } = require('..')\n\ntest('SnapshotAgent - integration with redirect interceptor', async (t) => {\n  const snapshotPath = join(tmpdir(), `test-snapshot-redirect-${Date.now()}.json`)\n  const originalDispatcher = getGlobalDispatcher()\n\n  t.after(() => unlink(snapshotPath).catch(() => {}))\n  t.after(() => setGlobalDispatcher(originalDispatcher))\n\n  // Create a server that handles redirects\n  const server = createServer((req, res) => {\n    if (req.url === '/redirect-start') {\n      res.writeHead(302, { location: '/redirect-target' })\n      res.end('Redirecting...')\n    } else if (req.url === '/redirect-target') {\n      res.writeHead(200, { 'content-type': 'application/json' })\n      res.end(JSON.stringify({ message: 'Final destination' }))\n    } else {\n      res.writeHead(404)\n      res.end('Not Found')\n    }\n  })\n\n  await promisify(server.listen.bind(server))(0)\n  const { port } = server.address()\n  const origin = `http://localhost:${port}`\n\n  t.after(() => server.close())\n\n  // Demonstrates the intended usage pattern: SnapshotAgent and redirect interceptor together\n  const { interceptors, Agent } = require('..')\n\n  // First use redirect interceptor to capture the complete redirect flow\n  const redirectAgent = new Agent().compose(interceptors.redirect({ maxRedirections: 5 }))\n  setGlobalDispatcher(redirectAgent)\n\n  const redirectResponse = await request(`${origin}/redirect-start`)\n  const redirectBody = await redirectResponse.body.json()\n\n  // Verify redirect worked\n  assert.strictEqual(redirectResponse.statusCode, 200)\n  assert.deepStrictEqual(redirectBody, { message: 'Final destination' })\n  assert(redirectResponse.context && redirectResponse.context.history)\n  assert.strictEqual(redirectResponse.context.history.length, 2)\n\n  await redirectAgent.close()\n\n  // Record redirected responses using SnapshotAgent with redirect interceptor\n  // This tests the fixed integration where SnapshotAgent automatically records final responses\n  const recordingAgent = new SnapshotAgent({\n    mode: 'record',\n    snapshotPath\n  }).compose(interceptors.redirect({ maxRedirections: 5 }))\n\n  setGlobalDispatcher(recordingAgent)\n\n  // Make request to redirect URL - should automatically record the final response\n  const recordingResponse = await request(`${origin}/redirect-start`)\n  const recordingBody = await recordingResponse.body.json()\n\n  // Verify that we got the final response (not the 302)\n  assert.strictEqual(recordingResponse.statusCode, 200)\n  assert.deepStrictEqual(recordingBody, { message: 'Final destination' })\n  // Note: context.history is not preserved in SnapshotAgent recording mode\n  // since we capture the final response directly\n\n  await recordingAgent.close()\n\n  // Playback mode - SnapshotAgent provides recorded responses\n  // In playback mode, SnapshotAgent returns the recorded final response directly\n  // Also include redirect interceptor to handle any redirect scenarios consistently\n  const playbackAgent = new SnapshotAgent({\n    mode: 'playback',\n    snapshotPath\n  }).compose(interceptors.redirect({ maxRedirections: 5 }))\n\n  setGlobalDispatcher(playbackAgent)\n\n  // This should return the recorded final response directly from snapshot\n  const playbackResponse = await request(`${origin}/redirect-start`)\n  const playbackBody = await playbackResponse.body.json()\n\n  assert.strictEqual(playbackResponse.statusCode, 200)\n  assert.deepStrictEqual(playbackBody, { message: 'Final destination' })\n\n  // In playback mode, context is not preserved since we're replaying recorded responses\n  // The important thing is that we get the correct final response content\n\n  // Verify the snapshot recorded the redirect request with final response\n  const playbackRecorder = playbackAgent.getRecorder()\n  assert.strictEqual(playbackRecorder.size(), 2, 'Should have two snapshots')\n\n  const snapshots = playbackRecorder.getSnapshots()\n\n  {\n    const snapshot = snapshots[0]\n    assert.strictEqual(snapshot.request.url, `${origin}/redirect-start`)\n    assert.strictEqual(snapshot.responses[0].statusCode, 302)\n    assert.strictEqual(Buffer.from(snapshot.responses[0].body, 'base64').toString(), 'Redirecting...')\n  }\n\n  {\n    const snapshot = snapshots[1]\n    assert.strictEqual(snapshot.request.url, `${origin}/redirect-target`)\n    assert.strictEqual(snapshot.responses[0].statusCode, 200)\n    assert.deepStrictEqual(JSON.parse(Buffer.from(snapshot.responses[0].body, 'base64')), {\n      message: 'Final destination'\n    })\n  }\n\n  await playbackAgent.close()\n})\n"
  },
  {
    "path": "test/snapshot-testing.js",
    "content": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert')\nconst { createServer } = require('node:http')\nconst { promisify } = require('node:util')\nconst { unlink, writeFile, readFile } = require('node:fs/promises')\nconst { tmpdir } = require('node:os')\nconst { join } = require('node:path')\nconst { SnapshotAgent, setGlobalDispatcher, getGlobalDispatcher, request } = require('..')\n\n// Test constants\nconst TEST_CONSTANTS = {\n  KEEP_ALIVE_TIMEOUT: 10,\n  KEEP_ALIVE_MAX_TIMEOUT: 10,\n  AUTO_FLUSH_INTERVAL: 100,\n  SEQUENTIAL_RESPONSE_DELAY: 200,\n  TEST_TIMESTAMP: '2024-01-01T00:00:00Z',\n  TEST_MESSAGE: 'Hello World',\n  MAX_SNAPSHOTS_FOR_LRU: 2,\n  TEST_ORIGINS: {\n    LOCALHOST_3000: 'http://localhost:3000'\n  },\n  ERROR_MESSAGES: {\n    INVALID_MODE: 'Invalid snapshot mode: invalid. Must be one of: record, playback, update',\n    MISSING_SNAPSHOT_PATH_PLAYBACK: \"snapshotPath is required when mode is 'playback'\",\n    MISSING_SNAPSHOT_PATH_UPDATE: \"snapshotPath is required when mode is 'update'\",\n    NO_SNAPSHOT_FOUND: 'No snapshot found for GET /nonexistent'\n  }\n}\n\n// Test helper functions\nfunction createSnapshotPath (prefix = 'test-snapshots') {\n  return join(tmpdir(), `${prefix}-${Date.now()}.json`)\n}\n\nfunction createTestServer (handler) {\n  return createServer(handler)\n}\n\nasync function setupServer (server) {\n  await promisify(server.listen.bind(server))(0)\n  const { port } = server.address()\n  const origin = `http://localhost:${port}`\n  return { port, origin }\n}\n\nfunction setupCleanup (t, resources) {\n  if (resources.server) {\n    t.after(() => {\n      resources.server.closeAllConnections?.()\n      resources.server.close()\n    })\n  }\n  if (resources.snapshotPath) {\n    t.after(() => unlink(resources.snapshotPath).catch(() => {}))\n  }\n  if (resources.agent) {\n    t.after(async () => await resources.agent.close())\n  }\n  if (resources.originalDispatcher) {\n    t.after(() => setGlobalDispatcher(resources.originalDispatcher))\n  }\n}\n\nfunction createJsonResponse (data) {\n  return JSON.stringify(data)\n}\n\nfunction createDefaultHandler () {\n  return (req, res) => {\n    if (req.url === '/test') {\n      res.writeHead(200, { 'content-type': 'application/json' })\n      res.end(createJsonResponse({\n        message: TEST_CONSTANTS.TEST_MESSAGE,\n        timestamp: TEST_CONSTANTS.TEST_TIMESTAMP\n      }))\n    } else {\n      res.writeHead(404)\n      res.end('Not Found')\n    }\n  }\n}\n\nfunction createEchoHandler () {\n  return (req, res) => {\n    let body = ''\n    req.on('data', chunk => { body += chunk })\n    req.on('end', async (t) => {\n      res.writeHead(200, { 'content-type': 'application/json' })\n      res.end(createJsonResponse({\n        received: body,\n        method: req.method,\n        headers: req.headers\n      }))\n    })\n  }\n}\n\nfunction createSequentialHandler (responses) {\n  let callCount = 0\n  return (req, res) => {\n    res.writeHead(200, { 'content-type': 'text/plain' })\n    res.end(responses[callCount++] || responses[responses.length - 1])\n  }\n}\n\nasync function createLargeSnapshotFile (path, size = 1000) {\n  const { createRequestHash, formatRequestKey, createHeaderFilters } = require('../lib/mock/snapshot-recorder')\n\n  const snapshots = []\n  for (let i = 0; i < size; i++) {\n    const requestOpts = {\n      origin: 'http://localhost:3000',\n      path: `/api/test-${i}`,\n      method: 'GET'\n    }\n\n    const cachedSets = createHeaderFilters({})\n    const requestKey = formatRequestKey(requestOpts, cachedSets)\n    const hash = createRequestHash(requestKey)\n\n    snapshots.push({\n      hash,\n      snapshot: {\n        request: requestKey,\n        responses: [{\n          statusCode: 200,\n          headers: { 'content-type': 'application/json' },\n          body: Buffer.from(`{\"data\": \"test-${i}\"}`).toString('base64'),\n          trailers: {}\n        }],\n        callCount: 0,\n        timestamp: new Date().toISOString()\n      }\n    })\n  }\n\n  await writeFile(path, JSON.stringify(snapshots, null, 2))\n}\n\n// Organize tests with describe blocks\ndescribe('SnapshotAgent - Basic Operations', () => {\n  it('record mode', async (t) => {\n    const server = createTestServer(createDefaultHandler())\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('record-mode')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      keepAliveTimeout: TEST_CONSTANTS.KEEP_ALIVE_TIMEOUT,\n      keepAliveMaxTimeout: TEST_CONSTANTS.KEEP_ALIVE_MAX_TIMEOUT,\n      mode: 'record',\n      snapshotPath\n    })\n\n    // Make a request that should be recorded\n    const response = await request(`${origin}/test`, {\n      dispatcher: agent\n    })\n    const body = await response.body.json()\n\n    assert.strictEqual(response.statusCode, 200, 'Response should have status 200')\n    assert.deepStrictEqual(body, {\n      message: TEST_CONSTANTS.TEST_MESSAGE,\n      timestamp: TEST_CONSTANTS.TEST_TIMESTAMP\n    }, 'Response body should match expected data')\n\n    // Save snapshots\n    await agent.saveSnapshots()\n\n    // Verify snapshot was recorded\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should have recorded exactly one snapshot')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots.length, 1, 'Snapshots array should contain one item')\n    assert.strictEqual(snapshots[0].request.method, 'GET', 'Recorded request method should be GET')\n    assert.strictEqual(snapshots[0].request.url, `${origin}/test`, 'Recorded request URL should match')\n    assert.strictEqual(snapshots[0].responses[0].statusCode, 200, 'Recorded response status should be 200')\n  })\n\n  it('playback mode', async (t) => {\n    const snapshotPath = createSnapshotPath('playback-mode')\n    setupCleanup(t, { snapshotPath })\n\n    // First, create a recording\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    // Create a simple server for recording\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end('Recorded response')\n    })\n\n    const { origin } = await setupServer(server)\n    const originalDispatcher = getGlobalDispatcher()\n\n    setupCleanup(t, { server, originalDispatcher })\n    setGlobalDispatcher(recordingAgent)\n\n    // Record the request\n    await request(`${origin}/api/test`)\n    await recordingAgent.saveSnapshots()\n\n    // Now test playback mode\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath\n    })\n\n    setGlobalDispatcher(playbackAgent)\n\n    // This should use the recorded response, not make a real request\n    const response = await request(`${origin}/api/test`)\n    const body = await response.body.text()\n\n    assert.strictEqual(response.statusCode, 200, 'Playback response should have status 200')\n    assert.strictEqual(body, 'Recorded response', 'Playback should return recorded response')\n  })\n\n  it('update mode', async (t) => {\n    const snapshotPath = createSnapshotPath('update-mode')\n    setupCleanup(t, { snapshotPath })\n\n    // Create agent in update mode\n    const agent = new SnapshotAgent({\n      mode: 'update',\n      snapshotPath\n    })\n\n    // Create a simple server\n    const server = createTestServer((req, res) => {\n      if (req.url === '/existing') {\n        res.writeHead(200, { 'content-type': 'text/plain' })\n        res.end('Existing endpoint')\n      } else if (req.url === '/new') {\n        res.writeHead(200, { 'content-type': 'text/plain' })\n        res.end('New endpoint')\n      } else {\n        res.writeHead(404)\n        res.end('Not Found')\n      }\n    })\n\n    const { origin } = await setupServer(server)\n    const originalDispatcher = getGlobalDispatcher()\n\n    setupCleanup(t, { server, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // First request - should be recorded as new\n    const response1 = await request(`${origin}/existing`)\n    const body1 = await response1.body.text()\n    assert.strictEqual(body1, 'Existing endpoint', 'First request should get live response')\n\n    // Save and reload to simulate existing snapshots\n    await agent.saveSnapshots()\n\n    // Second request to same endpoint - should use existing snapshot\n    const response2 = await request(`${origin}/existing`)\n    const body2 = await response2.body.text()\n    assert.strictEqual(body2, 'Existing endpoint', 'Second request should use cached response')\n\n    // Request to new endpoint - should be recorded\n    const response3 = await request(`${origin}/new`)\n    const body3 = await response3.body.text()\n    assert.strictEqual(body3, 'New endpoint', 'New endpoint should get live response')\n\n    // Verify we have 2 different snapshots\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 2, 'Should have exactly two snapshots recorded')\n  })\n})\n\ndescribe('SnapshotAgent - Request Handling', () => {\n  it('handles POST requests with body', async (t) => {\n    const snapshotPath = createSnapshotPath('post-requests')\n    setupCleanup(t, { snapshotPath })\n\n    const server = createTestServer(createEchoHandler())\n    const { origin } = await setupServer(server)\n\n    setupCleanup(t, { server })\n\n    // Record mode\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { originalDispatcher })\n    setGlobalDispatcher(recordingAgent)\n\n    const requestBody = createJsonResponse({ test: 'data' })\n    const response = await request(`${origin}/api/submit`, {\n      method: 'POST',\n      headers: { 'content-type': 'application/json' },\n      body: requestBody\n    })\n\n    const responseBody = await response.body.json()\n    assert.strictEqual(responseBody.received, requestBody, 'Server should receive the request body')\n    assert.strictEqual(responseBody.method, 'POST', 'Server should receive POST method')\n\n    await recordingAgent.saveSnapshots()\n\n    // Playback mode\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath\n    })\n\n    setGlobalDispatcher(playbackAgent)\n\n    // Make the same request - should get recorded response\n    const playbackResponse = await request(`${origin}/api/submit`, {\n      method: 'POST',\n      headers: { 'content-type': 'application/json' },\n      body: requestBody\n    })\n\n    const playbackBody = await playbackResponse.body.json()\n    assert.strictEqual(playbackBody.received, requestBody, 'Playback should return recorded request body')\n    assert.strictEqual(playbackBody.method, 'POST', 'Playback should return recorded method')\n  })\n\n  it('sequential response support', async (t) => {\n    const responses = ['First response', 'Second response', 'Third response']\n    const server = createTestServer(createSequentialHandler(responses))\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('sequential')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    // Record multiple responses to the same endpoint\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { originalDispatcher })\n    setGlobalDispatcher(recordingAgent)\n\n    // Make multiple requests to record sequential responses\n    {\n      const res = await request(`${origin}/api/test`)\n      await res.body.text()\n    }\n    {\n      const res = await request(`${origin}/api/test`)\n      await res.body.text()\n    }\n    {\n      const res = await request(`${origin}/api/test`)\n      await res.body.text()\n    }\n\n    // Ensure all recordings are saved and verify the recording state\n    await recordingAgent.saveSnapshots()\n\n    // Verify recording worked correctly before switching to playback\n    const recordingRecorder = recordingAgent.getRecorder()\n    assert.strictEqual(recordingRecorder.size(), 1, 'Should have recorded exactly one snapshot')\n    const recordedSnapshots = recordingRecorder.getSnapshots()\n    assert.strictEqual(recordedSnapshots[0].responses.length, 3, 'Should have recorded three responses')\n\n    // Close recording agent cleanly before starting playback\n    await recordingAgent.close()\n\n    // Switch to playback mode and test sequential responses\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    // Ensure snapshots are loaded and call counts are reset before setting dispatcher\n    await playbackAgent.loadSnapshots()\n\n    // Reset call counts after loading to ensure clean state\n    playbackAgent.resetCallCounts()\n\n    // Verify we have the expected snapshots before proceeding\n    const recorder = playbackAgent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should have exactly one snapshot loaded')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots.length, 1, 'Should have exactly one snapshot')\n    assert.strictEqual(snapshots[0].responses.length, 3, 'Should have three sequential responses')\n\n    // Test sequential responses\n    const response1 = await request(`${origin}/api/test`)\n    const body1 = await response1.body.text()\n    assert.strictEqual(body1, 'First response', 'First call should return first response')\n\n    const response2 = await request(`${origin}/api/test`)\n    const body2 = await response2.body.text()\n    assert.strictEqual(body2, 'Second response', 'Second call should return second response')\n\n    const response3 = await request(`${origin}/api/test`)\n    const body3 = await response3.body.text()\n    assert.strictEqual(body3, 'Third response', 'Third call should return third response')\n\n    // Fourth call should repeat the last response\n    const response4 = await request(`${origin}/api/test`)\n    const body4 = await response4.body.text()\n    assert.strictEqual(body4, 'Third response', 'Fourth call should repeat the last response')\n  })\n})\n\ndescribe('SnapshotAgent - Error Handling', () => {\n  it('error handling in playback mode', async (t) => {\n    const snapshotPath = createSnapshotPath('error-handling')\n    setupCleanup(t, { snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath // File doesn't exist\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // This should throw because no snapshot exists for this request\n    let errorThrown = false\n    try {\n      await request('http://localhost:9999/nonexistent')\n    } catch (error) {\n      errorThrown = true\n      assert.strictEqual(error.name, 'UndiciError', 'Error should be UndiciError')\n      assert(error.message.includes(TEST_CONSTANTS.ERROR_MESSAGES.NO_SNAPSHOT_FOUND),\n        'Error message should indicate no snapshot found')\n      assert.strictEqual(error.code, 'UND_ERR', 'Error code should be UND_ERR')\n    }\n\n    assert(errorThrown, 'Expected an error to be thrown for missing snapshot')\n  })\n\n  it('constructor options validation', async (t) => {\n    // Test invalid mode\n    assert.throws(() => {\n      return new SnapshotAgent({ mode: 'invalid' })\n    }, {\n      name: 'InvalidArgumentError',\n      message: new RegExp(TEST_CONSTANTS.ERROR_MESSAGES.INVALID_MODE.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n    }, 'Should throw for invalid mode')\n\n    // Test missing snapshotPath for playback mode\n    assert.throws(() => {\n      return new SnapshotAgent({ mode: 'playback' })\n    }, {\n      name: 'InvalidArgumentError',\n      message: new RegExp(TEST_CONSTANTS.ERROR_MESSAGES.MISSING_SNAPSHOT_PATH_PLAYBACK)\n    }, 'Should throw for missing snapshotPath in playback mode')\n\n    // Test missing snapshotPath for update mode\n    assert.throws(() => {\n      return new SnapshotAgent({ mode: 'update' })\n    }, {\n      name: 'InvalidArgumentError',\n      message: new RegExp(TEST_CONSTANTS.ERROR_MESSAGES.MISSING_SNAPSHOT_PATH_UPDATE)\n    }, 'Should throw for missing snapshotPath in update mode')\n\n    // Test valid configurations should not throw\n    await assert.doesNotReject(async () => {\n      const agent1 = new SnapshotAgent({ mode: 'record' })\n      await agent1.close()\n    }, 'Should not throw for valid record mode')\n\n    await assert.doesNotReject(async () => {\n      const snapshotPath = createSnapshotPath('valid-playback')\n      const agent2 = new SnapshotAgent({ mode: 'playback', snapshotPath })\n      await agent2.close()\n    }, 'Should not throw for valid playback mode')\n\n    await assert.doesNotReject(async () => {\n      const snapshotPath = createSnapshotPath('valid-update')\n      const agent3 = new SnapshotAgent({ mode: 'update', snapshotPath })\n      await agent3.close()\n    }, 'Should not throw for valid update mode')\n  })\n})\n\ndescribe('SnapshotAgent - Edge Cases', () => {\n  it('handles large snapshot files', async (t) => {\n    const snapshotPath = createSnapshotPath('large')\n    setupCleanup(t, { snapshotPath })\n\n    await createLargeSnapshotFile(snapshotPath, 100)\n\n    const agent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n\n    // Should load large files without issues\n    await agent.loadSnapshots()\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 100, 'Should load all 100 snapshots from large file')\n\n    setGlobalDispatcher(agent)\n\n    // Should be able to find and use snapshots from large file\n    const response = await request('http://localhost:3000/api/test-0')\n    const body = await response.body.json()\n    assert.deepStrictEqual(body, { data: 'test-0' }, 'Should return correct data from large snapshot file')\n  })\n\n  it('concurrent access scenarios', async (t) => {\n    const snapshotPath = createSnapshotPath('concurrent')\n    setupCleanup(t, { snapshotPath })\n\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'application/json' })\n      res.end(createJsonResponse({ path: req.url }))\n    })\n\n    const { origin } = await setupServer(server)\n    setupCleanup(t, { server })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make multiple concurrent requests\n    const promises = []\n    for (let i = 0; i < 10; i++) {\n      promises.push(request(`${origin}/api/test-${i}`))\n    }\n\n    const responses = await Promise.all(promises)\n\n    // Verify all responses were handled correctly\n    for (let i = 0; i < responses.length; i++) {\n      const body = await responses[i].body.json()\n      assert.deepStrictEqual(body, { path: `/api/test-${i}` },\n        `Concurrent request ${i} should return correct response`)\n    }\n\n    await agent.saveSnapshots()\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 10, 'Should record all 10 concurrent requests')\n  })\n})\n\ndescribe('SnapshotAgent - Advanced Features', () => {\n  it('snapshot file format validation', async (t) => {\n    const snapshotPath = createSnapshotPath('format-validation')\n    setupCleanup(t, { snapshotPath })\n\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'x-custom-header': 'test-value' })\n      res.end('Test response')\n    })\n\n    const { origin } = await setupServer(server)\n    setupCleanup(t, { server })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    await request(`${origin}/test-endpoint`)\n    await agent.saveSnapshots()\n\n    // Read and verify the snapshot file format\n    const snapshotData = JSON.parse(await readFile(snapshotPath, 'utf8'))\n\n    assert(Array.isArray(snapshotData), 'Snapshot data should be an array')\n    assert.strictEqual(snapshotData.length, 1, 'Should contain exactly one snapshot')\n\n    const snapshot = snapshotData[0]\n    assert(typeof snapshot.hash === 'string', 'Snapshot should have string hash')\n    assert(typeof snapshot.snapshot === 'object', 'Snapshot should have snapshot object')\n\n    const { request: req, responses, timestamp } = snapshot.snapshot\n    assert.strictEqual(req.method, 'GET', 'Request method should be GET')\n    assert.strictEqual(req.url, `${origin}/test-endpoint`, 'Request URL should match')\n    assert.strictEqual(responses[0].statusCode, 200, 'Response status should be 200')\n\n    // Headers should be normalized to lowercase\n    assert(responses[0].headers['x-custom-header'], 'Custom header should be present')\n    assert.strictEqual(responses[0].headers['x-custom-header'], 'test-value', 'Custom header value should match')\n    assert(typeof timestamp === 'string', 'Timestamp should be a string')\n  })\n\n  it('maxSnapshots and LRU eviction', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('lru-eviction')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      maxSnapshots: TEST_CONSTANTS.MAX_SNAPSHOTS_FOR_LRU\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make 3 requests to trigger LRU eviction\n    await request(`${origin}/first`)\n    await request(`${origin}/second`)\n    await request(`${origin}/third`)\n\n    const recorder = agent.getRecorder()\n\n    // Should only have 2 snapshots due to LRU eviction\n    assert.strictEqual(recorder.size(), TEST_CONSTANTS.MAX_SNAPSHOTS_FOR_LRU,\n      `Should only keep ${TEST_CONSTANTS.MAX_SNAPSHOTS_FOR_LRU} snapshots due to LRU eviction`)\n\n    const snapshots = recorder.getSnapshots()\n    const urls = snapshots.map(s => s.request.url)\n\n    // First snapshot should be evicted, should have second and third\n    assert(urls.includes(`${origin}/second`), 'Should contain second request')\n    assert(urls.includes(`${origin}/third`), 'Should contain third request')\n    assert(!urls.includes(`${origin}/first`), 'Should not contain first request (evicted)')\n  })\n\n  it('auto-flush functionality', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end('Auto-flush test')\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('auto-flush')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      autoFlush: true,\n      flushInterval: TEST_CONSTANTS.AUTO_FLUSH_INTERVAL\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make a request\n    await request(`${origin}/autoflush-test`)\n\n    // Wait for auto-flush to trigger and ensure it completes\n    await new Promise(resolve => setTimeout(resolve, TEST_CONSTANTS.SEQUENTIAL_RESPONSE_DELAY))\n\n    // Force a final flush to ensure all data is written\n    await agent.saveSnapshots()\n\n    // Verify file was written automatically\n    const fileData = await readFile(snapshotPath, 'utf8')\n    const snapshots = JSON.parse(fileData)\n\n    assert(Array.isArray(snapshots), 'Auto-flushed data should be an array')\n    assert.strictEqual(snapshots.length, 1, 'Should contain exactly one auto-flushed snapshot')\n    assert.strictEqual(snapshots[0].snapshot.request.url, `${origin}/autoflush-test`,\n      'Auto-flushed snapshot should have correct URL')\n  })\n})\n\ndescribe('SnapshotAgent - Header Management', () => {\n  it('custom header matching with matchHeaders', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, {\n        'content-type': 'application/json',\n        'x-request-id': '12345',\n        authorization: 'Bearer secret-token'\n      })\n      res.end('{\"message\": \"test\"}')\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('match-headers')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      matchHeaders: ['content-type'] // Only match on content-type header\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make first request with authorization header\n    await request(`${origin}/test`, {\n      headers: {\n        authorization: 'Bearer secret-token',\n        'content-type': 'application/json'\n      }\n    })\n\n    // Save snapshots before switching to playback\n    await agent.saveSnapshots()\n\n    // Make second request with different authorization but same content-type\n    // This should match the first request due to matchHeaders config\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      matchHeaders: ['content-type']\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    const response = await request(`${origin}/test`, {\n      headers: {\n        authorization: 'Bearer different-token', // Different auth token\n        'content-type': 'application/json' // Same content-type\n      }\n    })\n\n    assert.strictEqual(response.statusCode, 200, 'Should match despite different auth token')\n    const body = await response.body.text()\n    assert.strictEqual(body, '{\"message\": \"test\"}', 'Should return recorded response')\n  })\n\n  it('ignore headers functionality', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end('ignore headers test')\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('ignore-headers')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      ignoreHeaders: ['authorization', 'x-request-id'] // Ignore these for matching\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make first request\n    await request(`${origin}/test`, {\n      headers: {\n        authorization: 'Bearer token1',\n        'x-request-id': 'req-123',\n        'content-type': 'application/json'\n      }\n    })\n\n    // Save snapshots before switching to playback\n    await agent.saveSnapshots()\n\n    // Switch to playback mode and make request with different ignored headers\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      ignoreHeaders: ['authorization', 'x-request-id']\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    const response = await request(`${origin}/test`, {\n      headers: {\n        authorization: 'Bearer different-token', // Different (ignored)\n        'x-request-id': 'req-456', // Different (ignored)\n        'content-type': 'application/json' // Same (not ignored)\n      }\n    })\n\n    assert.strictEqual(response.statusCode, 200, 'Should match despite different ignored headers')\n    const body = await response.body.text()\n    assert.strictEqual(body, 'ignore headers test', 'Should return recorded response')\n  })\n\n  it('exclude headers for security', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, {\n        'content-type': 'application/json',\n        'set-cookie': 'session=secret123; HttpOnly',\n        authorization: 'Bearer server-token'\n      })\n      res.end('{\"data\": \"sensitive\"}')\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('exclude-headers')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      excludeHeaders: ['authorization', 'set-cookie'] // Don't store these sensitive headers\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    await request(`${origin}/test`)\n    await agent.saveSnapshots()\n\n    // Read snapshot file and verify sensitive headers are not stored\n    const fileData = await readFile(snapshotPath, 'utf8')\n    const snapshots = JSON.parse(fileData)\n\n    assert.strictEqual(snapshots.length, 1, 'Should contain exactly one snapshot')\n    const snapshot = snapshots[0].snapshot\n\n    // Verify excluded headers are not in stored response\n    assert(!snapshot.responses[0].headers.authorization, 'Authorization header should be excluded from storage')\n    assert(!snapshot.responses[0].headers['set-cookie'], 'Set-Cookie header should be excluded from storage')\n    assert(snapshot.responses[0].headers['content-type'], 'Content-Type header should be preserved')\n  })\n})\n\ndescribe('SnapshotAgent - Request Matching', () => {\n  it('query parameter matching control', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('query-matching')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      matchQuery: false // Ignore query parameters in matching\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Record request with query parameters\n    await request(`${origin}/api/data?timestamp=123&session=abc`)\n\n    // Save snapshots before switching to playback\n    await agent.saveSnapshots()\n\n    // Switch to playback with different query parameters\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      matchQuery: false\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    // This should match the recorded request despite different query params\n    const response = await request(`${origin}/api/data?timestamp=456&session=xyz`)\n\n    assert.strictEqual(response.statusCode, 200, 'Should match despite different query parameters')\n    const body = await response.body.text()\n    assert.strictEqual(body, 'Response for /api/data?timestamp=123&session=abc',\n      'Should return original recorded response with original query params')\n  })\n\n  it('body matching control', async (t) => {\n    const server = createTestServer((req, res) => {\n      let body = ''\n      req.on('data', chunk => { body += chunk })\n      req.on('end', async (t) => {\n        res.writeHead(200, { 'content-type': 'application/json' })\n        res.end(`{\"received\": \"${body}\"}`)\n      })\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('body-matching')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      matchBody: false // Ignore request body in matching\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Record request with specific body\n    await request(`${origin}/api/submit`, {\n      method: 'POST',\n      body: 'original-data',\n      headers: { 'content-type': 'text/plain' }\n    })\n\n    // Save snapshots before switching to playback\n    await agent.saveSnapshots()\n\n    // Switch to playback with different body\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      matchBody: false\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    // This should match despite different body content\n    const response = await request(`${origin}/api/submit`, {\n      method: 'POST',\n      body: 'different-data',\n      headers: { 'content-type': 'text/plain' }\n    })\n\n    assert.strictEqual(response.statusCode, 200, 'Should match despite different request body')\n    const responseBody = await response.body.json()\n    assert.strictEqual(responseBody.received, 'original-data',\n      'Should return recorded response with original body')\n  })\n})\n\ndescribe('SnapshotAgent - Management Features', () => {\n  it('call count reset functionality', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end('Test response')\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('reset-functionality')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Record a snapshot\n    await request(`${origin}/api/test`)\n    await agent.saveSnapshots()\n\n    // Check initial call count\n    const info1 = agent.getSnapshotInfo({\n      origin,\n      path: '/api/test',\n      method: 'GET'\n    })\n    assert(info1, 'Should find snapshot info')\n    assert.strictEqual(info1.callCount, 0, 'Call count should be 0 initially (only incremented during findSnapshot)')\n\n    // Reset call counts\n    agent.resetCallCounts()\n\n    const info2 = agent.getSnapshotInfo({\n      origin,\n      path: '/api/test',\n      method: 'GET'\n    })\n    assert(info2, 'Should still find snapshot info after reset')\n    assert.strictEqual(info2.callCount, 0, 'Call count should remain 0 after reset')\n  })\n\n  it('snapshot management methods', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('management-methods')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Record multiple snapshots\n    await request(`${origin}/api/users`)\n    await request(`${origin}/api/posts`)\n\n    // Test getSnapshotInfo\n    const userInfo = agent.getSnapshotInfo({\n      origin,\n      path: '/api/users',\n      method: 'GET'\n    })\n    assert(userInfo, 'Should find user snapshot info')\n    assert.strictEqual(userInfo.request.method, 'GET', 'User snapshot method should be GET')\n    assert.strictEqual(userInfo.request.url, `${origin}/api/users`, 'User snapshot URL should match')\n    assert.strictEqual(userInfo.responseCount, 1, 'User snapshot should have one response')\n\n    // Test deleteSnapshot\n    const deleted = agent.deleteSnapshot({\n      origin,\n      path: '/api/users',\n      method: 'GET'\n    })\n    assert.strictEqual(deleted, true, 'Should successfully delete user snapshot')\n\n    // Verify deletion\n    const deletedInfo = agent.getSnapshotInfo({\n      origin,\n      path: '/api/users',\n      method: 'GET'\n    })\n    assert.strictEqual(deletedInfo, null, 'Deleted snapshot should not be found')\n\n    // Post snapshot should still exist\n    const postInfo = agent.getSnapshotInfo({\n      origin,\n      path: '/api/posts',\n      method: 'GET'\n    })\n    assert(postInfo, 'Post snapshot should still exist after deleting user snapshot')\n\n    // Test replaceSnapshots - create a snapshot with proper hash\n    const { createRequestHash, formatRequestKey, createHeaderFilters } = require('../lib/mock/snapshot-recorder')\n    const mockRequestOpts = {\n      origin,\n      path: '/api/mock',\n      method: 'GET'\n    }\n    const cachedSets = createHeaderFilters({})\n    const mockRequest = formatRequestKey(mockRequestOpts, cachedSets)\n    const mockHash = createRequestHash(mockRequest)\n\n    const mockData = [\n      {\n        hash: mockHash,\n        snapshot: {\n          request: mockRequest,\n          responses: [{ statusCode: 200, headers: {}, body: 'bW9jaw==', trailers: {} }],\n          callCount: 0,\n          timestamp: new Date().toISOString()\n        }\n      }\n    ]\n\n    agent.replaceSnapshots(mockData)\n\n    // Should only have the mock snapshot now\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should have only one snapshot after replacement')\n\n    const mockInfo = agent.getSnapshotInfo(mockRequestOpts)\n    assert(mockInfo, 'Should find mock snapshot after replacement')\n    assert.strictEqual(mockInfo.request.url, `${origin}/api/mock`, 'Mock snapshot URL should match')\n  })\n})\n\ndescribe('SnapshotAgent - Filtering', () => {\n  it('shouldRecord filtering', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('should-record-filter')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      shouldRecord: (requestOpts) => {\n        // Only record requests to /api/allowed\n        return requestOpts.path === '/api/allowed'\n      }\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make requests - only one should be recorded\n    await request(`${origin}/api/allowed`)\n    await request(`${origin}/api/filtered`)\n\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should record only the allowed request')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots[0].request.url, `${origin}/api/allowed`,\n      'Recorded snapshot should be the allowed request')\n  })\n\n  it('shouldPlayback filtering', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Live response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('should-playback-filter')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    // First, record some snapshots without filtering\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent: recordingAgent, originalDispatcher })\n    setGlobalDispatcher(recordingAgent)\n\n    await request(`${origin}/api/cached`)\n    await request(`${origin}/api/live`)\n    await recordingAgent.saveSnapshots()\n\n    // Now test playback with filtering\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      shouldPlayback: (requestOpts) => {\n        // Only playback requests to /api/cached\n        return requestOpts.path === '/api/cached'\n      }\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    // This should use cached response\n    const cachedResponse = await request(`${origin}/api/cached`)\n    const cachedBody = await cachedResponse.body.text()\n    assert.strictEqual(cachedBody, 'Live response for /api/cached',\n      'Should return cached response for allowed path')\n\n    // This should fail because playback is filtered and no live server\n    // Need to close the recording server to ensure no fallback\n    server.close()\n    let errorThrown = false\n    try {\n      await request(`${origin}/api/live`)\n    } catch (error) {\n      errorThrown = true\n      assert.strictEqual(error.name, 'UndiciError', 'Should throw UndiciError for filtered request')\n    }\n\n    assert(errorThrown, 'Expected an error for filtered playback request')\n  })\n\n  it('URL exclusion patterns (string)', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('url-exclusion-string')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      excludeUrls: ['/private', 'secret']\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make requests - some should be excluded\n    await request(`${origin}/api/public`)\n    await request(`${origin}/private/data`)\n    await request(`${origin}/api/secret-endpoint`)\n\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should record only non-excluded requests')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots[0].request.url, `${origin}/api/public`,\n      'Should record only the public API request')\n  })\n\n  it('URL exclusion patterns (regex)', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('url-exclusion-regex')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      excludeUrls: [/\\/admin\\/.*/, /.*\\?token=.*/]\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make requests - some should be excluded by regex patterns\n    await request(`${origin}/api/data`)\n    await request(`${origin}/admin/users`)\n    await request(`${origin}/api/auth?token=secret`)\n\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should record only requests not matching exclusion patterns')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots[0].request.url, `${origin}/api/data`,\n      'Should record only the non-excluded API request')\n  })\n\n  it('complex filtering scenarios', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response for ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('complex-filtering')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      shouldRecord: (requestOpts) => {\n        // Only record GET requests\n        return (requestOpts.method || 'GET') === 'GET'\n      },\n      excludeUrls: ['/health']\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent, originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make various requests with multiple filters\n    await request(`${origin}/api/users`) // Should record (GET, not excluded)\n    await request(`${origin}/health`) // Should not record (excluded URL)\n    await request(`${origin}/api/data`, { method: 'POST' }) // Should not record (POST method)\n\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should record only requests passing all filters')\n\n    const snapshots = recorder.getSnapshots()\n    assert.strictEqual(snapshots[0].request.url, `${origin}/api/users`,\n      'Should record only the allowed GET request')\n    assert.strictEqual(snapshots[0].request.method, 'GET',\n      'Recorded request should have GET method')\n  })\n\n  it('excluded URLs should not error in playback mode', async (t) => {\n    const server = createTestServer((req, res) => {\n      res.writeHead(200, { 'content-type': 'text/plain' })\n      res.end(`Response from ${req.url}`)\n    })\n\n    const { origin } = await setupServer(server)\n    const snapshotPath = createSnapshotPath('exclude-playback-bug')\n\n    setupCleanup(t, { server, snapshotPath })\n\n    // Record mode: record one request, exclude another\n    const recordingAgent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      excludeUrls: [`${origin}/excluded`]\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { agent: recordingAgent, originalDispatcher })\n    setGlobalDispatcher(recordingAgent)\n\n    // Request to included endpoint - should be recorded\n    const res1 = await request(`${origin}/included`)\n    await res1.body.text()\n\n    // Request to excluded endpoint - should NOT be recorded\n    const res2 = await request(`${origin}/excluded`)\n    await res2.body.text()\n\n    await recordingAgent.saveSnapshots()\n\n    const recorder = recordingAgent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should have recorded only the included request')\n\n    // Playback mode: should allow excluded URL to pass through without error\n    const playbackAgent = new SnapshotAgent({\n      mode: 'playback',\n      snapshotPath,\n      excludeUrls: [`${origin}/excluded`]\n    })\n\n    setupCleanup(t, { agent: playbackAgent })\n    setGlobalDispatcher(playbackAgent)\n\n    // This should work - replays from snapshot\n    const res3 = await request(`${origin}/included`)\n    await res3.body.text()\n    assert.strictEqual(res3.statusCode, 200, 'Included request should replay successfully')\n\n    // Excluded URL should pass through to real server\n    const res4 = await request(`${origin}/excluded`)\n    const body4 = await res4.body.text()\n    assert.strictEqual(res4.statusCode, 200, 'Excluded request should pass through to real server')\n    assert.strictEqual(body4, 'Response from /excluded', 'Should get live response from server')\n  })\n})\n\ndescribe('SnapshotAgent - Close Method', () => {\n  it('close() saves recordings before cleanup', async (t) => {\n    const snapshotPath = createSnapshotPath('close-saves')\n    setupCleanup(t, { snapshotPath })\n\n    const server = createTestServer(createDefaultHandler())\n    const { origin } = await setupServer(server)\n    setupCleanup(t, { server })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath,\n      autoFlush: false // Disable auto-flush to test manual save on close\n    })\n\n    const originalDispatcher = getGlobalDispatcher()\n    setupCleanup(t, { originalDispatcher })\n    setGlobalDispatcher(agent)\n\n    // Make a request that should be recorded\n    await request(`${origin}/test`)\n\n    // Verify snapshot is in memory but not yet saved to file\n    const recorder = agent.getRecorder()\n    assert.strictEqual(recorder.size(), 1, 'Should have recorded one snapshot in memory')\n\n    // Check that file doesn't exist yet (since autoFlush is false)\n    let fileExists = false\n    try {\n      await readFile(snapshotPath)\n      fileExists = true\n    } catch {\n      // File doesn't exist, which is expected\n    }\n    assert.strictEqual(fileExists, false, 'File should not exist before close()')\n\n    // Close the agent - this should save the snapshots\n    await agent.close()\n\n    // Verify the snapshots were saved to file\n    let savedData\n    try {\n      const fileContent = await readFile(snapshotPath, 'utf8')\n      savedData = JSON.parse(fileContent)\n    } catch (error) {\n      assert.fail(`Failed to read saved snapshot file: ${error.message}`)\n    }\n\n    assert(Array.isArray(savedData), 'Saved data should be an array')\n    assert.strictEqual(savedData.length, 1, 'Should have saved one snapshot')\n    assert.strictEqual(savedData[0].snapshot.request.method, 'GET', 'Saved snapshot should have correct method')\n    assert.strictEqual(savedData[0].snapshot.request.url, `${origin}/test`, 'Saved snapshot should have correct URL')\n  })\n\n  it('close() works when no recordings exist', async (t) => {\n    const snapshotPath = createSnapshotPath('close-no-recordings')\n    setupCleanup(t, { snapshotPath })\n\n    const agent = new SnapshotAgent({\n      mode: 'record',\n      snapshotPath\n    })\n\n    // Close agent immediately without making any requests or setting as dispatcher\n    await assert.doesNotReject(async () => {\n      await agent.close()\n    }, 'Should not throw when closing agent with no recordings')\n\n    // Verify no file was created\n    let fileExists = false\n    try {\n      await readFile(snapshotPath)\n      fileExists = true\n    } catch {\n      // File doesn't exist, which is expected\n    }\n    assert.strictEqual(fileExists, false, 'No file should be created when no recordings exist')\n  })\n\n  it('close() works when no snapshot path is configured', async (t) => {\n    const agent = new SnapshotAgent({\n      mode: 'record'\n      // No snapshotPath provided\n    })\n\n    const server = createTestServer(createDefaultHandler())\n    const { origin } = await setupServer(server)\n    setupCleanup(t, { server })\n\n    const originalDispatcher = getGlobalDispatcher()\n    t.after(() => setGlobalDispatcher(originalDispatcher))\n    setGlobalDispatcher(agent)\n\n    // Make a request\n    await request(`${origin}/test`)\n\n    // Close should not throw even without snapshot path\n    await assert.doesNotReject(async () => {\n      await agent.close()\n    }, 'Should not throw when closing agent without snapshot path')\n  })\n\n  it('recorder close() method works independently', async (t) => {\n    const { SnapshotRecorder } = require('../lib/mock/snapshot-recorder')\n    const snapshotPath = createSnapshotPath('recorder-close')\n    setupCleanup(t, { snapshotPath })\n\n    const recorder = new SnapshotRecorder({\n      snapshotPath,\n      mode: 'record'\n    })\n\n    // Manually add a snapshot to test saving\n    await recorder.record(\n      { origin: 'http://test.com', path: '/api', method: 'GET' },\n      {\n        statusCode: 200,\n        headers: { 'content-type': 'application/json' },\n        body: Buffer.from('{\"test\": true}'),\n        trailers: {}\n      }\n    )\n\n    assert.strictEqual(recorder.size(), 1, 'Should have one recorded snapshot')\n\n    // Close the recorder\n    await recorder.close()\n\n    // Verify the snapshot was saved\n    let savedData\n    try {\n      const fileContent = await readFile(snapshotPath, 'utf8')\n      savedData = JSON.parse(fileContent)\n    } catch (error) {\n      assert.fail(`Failed to read saved snapshot file: ${error.message}`)\n    }\n\n    assert(Array.isArray(savedData), 'Saved data should be an array')\n    assert.strictEqual(savedData.length, 1, 'Should have saved one snapshot')\n    assert.strictEqual(savedData[0].snapshot.request.method, 'GET', 'Should have correct method')\n  })\n})\n"
  },
  {
    "path": "test/socket-back-pressure.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { once } = require('node:events')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst { test, after } = require('node:test')\n\ntest('socket back-pressure', async (t) => {\n  t = tspl(t, { plan: 3 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  let bytesWritten = 0\n\n  const buf = Buffer.allocUnsafe(16384)\n  const src = new Readable({\n    read () {\n      bytesWritten += buf.length\n      this.push(buf)\n      if (bytesWritten >= 1e6) {\n        this.push(null)\n      }\n    }\n  })\n\n  server.on('request', (req, res) => {\n    src.pipe(res)\n  })\n  after(() => server.close())\n\n  server.listen(0)\n\n  await once(server, 'listening')\n  const client = new Client(`http://localhost:${server.address().port}`, {\n    pipelining: 1\n  })\n  after(() => client.close())\n\n  client.on('disconnect', () => {\n    if (!client.closed && !client.destroyed) {\n      t.fail('unexpected disconnect')\n    }\n  })\n\n  client.request({ path: '/', method: 'GET', opaque: 'asd' }, (err, data) => {\n    t.ifError(err)\n    data.body\n      .resume()\n      .once('data', () => {\n        data.body.pause()\n        // TODO: Try to avoid timeout.\n        setTimeout(() => {\n          t.ok(data.body._readableState.length < bytesWritten - data.body._readableState.highWaterMark)\n          src.push(null)\n          data.body.resume()\n        }, 1e3)\n      })\n      .on('end', () => {\n        t.ok(true, 'pass')\n      })\n  })\n  await t.completed\n})\n"
  },
  {
    "path": "test/socket-timeout.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst { createServer } = require('node:http')\nconst FakeTimers = require('@sinonjs/fake-timers')\n\ntest('timeout with pipelining 1', async (t) => {\n  t = tspl(t, { plan: 9 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n\n  server.once('request', (req, res) => {\n    t.ok(true, 'first request received, we are letting this timeout on the client')\n\n    server.once('request', (req, res) => {\n      t.strictEqual('/', req.url)\n      t.strictEqual('GET', req.method)\n      res.setHeader('content-type', 'text/plain')\n      res.end('hello')\n    })\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      pipelining: 1,\n      headersTimeout: 500,\n      bodyTimeout: 500\n    })\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      opaque: 'asd'\n    }, (err, data) => {\n      t.ok(err instanceof errors.HeadersTimeoutError) // we are expecting an error\n      t.strictEqual(data.opaque, 'asd')\n    })\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, { statusCode, headers, body }) => {\n      t.ifError(err)\n      t.strictEqual(statusCode, 200)\n      t.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n\ntest('Disable socket timeout', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true })\n  const clock = FakeTimers.install()\n  after(clock.uninstall.bind(clock))\n\n  server.once('request', (req, res) => {\n    setTimeout(() => {\n      res.end('hello')\n    }, 31e3)\n    clock.tick(32e3)\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`, {\n      bodyTimeout: 0,\n      headersTimeout: 0\n    })\n    after(() => client.close())\n\n    client.request({ path: '/', method: 'GET' }, (err, result) => {\n      t.ifError(err)\n      const bufs = []\n      result.body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      result.body.on('end', () => {\n        t.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/socks5-client.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst net = require('node:net')\nconst { Socks5Client, STATES, AUTH_METHODS, REPLY_CODES } = require('../lib/core/socks5-client')\nconst { InvalidArgumentError, Socks5ProxyError } = require('../lib/core/errors')\n\ntest('Socks5Client - constructor validation', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  p.throws(() => {\n    // eslint-disable-next-line no-new\n    new Socks5Client()\n  }, InvalidArgumentError, 'should throw when socket is not provided')\n\n  await p.completed\n})\n\ntest('Socks5Client - handshake flow', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  // Create a mock SOCKS5 server\n  const server = net.createServer((socket) => {\n    socket.on('data', (data) => {\n      // First message should be handshake\n      if (data[0] === 0x05 && data.length === 3) {\n        p.equal(data[0], 0x05, 'should send SOCKS version 5')\n        p.equal(data[1], 1, 'should send 1 auth method')\n        p.equal(data[2], AUTH_METHODS.NO_AUTH, 'should send NO_AUTH method')\n\n        // Send response accepting NO_AUTH\n        socket.write(Buffer.from([0x05, AUTH_METHODS.NO_AUTH]))\n      }\n    })\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, '127.0.0.1', resolve)\n  })\n\n  const { port } = server.address()\n  const socket = net.connect(port, '127.0.0.1')\n\n  await new Promise((resolve) => {\n    socket.on('connect', resolve)\n  })\n\n  const client = new Socks5Client(socket)\n\n  p.equal(client.state, STATES.INITIAL, 'should start in INITIAL state')\n\n  client.on('authenticated', () => {\n    p.equal(client.state, STATES.HANDSHAKING, 'should be in HANDSHAKING state after auth')\n    p.ok(true, 'should emit authenticated event')\n  })\n\n  await client.handshake()\n\n  // Wait for the authenticated event\n  await new Promise((resolve) => {\n    if (client.state !== STATES.HANDSHAKING) {\n      resolve()\n    } else {\n      client.once('authenticated', resolve)\n    }\n  })\n\n  socket.destroy()\n  server.close()\n\n  await p.completed\n})\n\ntest('Socks5Client - username/password authentication', async (t) => {\n  const p = tspl(t, { plan: 7 })\n\n  const testUsername = 'testuser'\n  const testPassword = 'testpass'\n\n  // Create a mock SOCKS5 server with auth\n  const server = net.createServer((socket) => {\n    let stage = 'handshake'\n\n    socket.on('data', (data) => {\n      if (stage === 'handshake' && data[0] === 0x05) {\n        p.equal(data[0], 0x05, 'should send SOCKS version 5')\n        p.equal(data[1], 2, 'should send 2 auth methods')\n        p.equal(data[2], AUTH_METHODS.USERNAME_PASSWORD, 'should send USERNAME_PASSWORD first')\n        p.equal(data[3], AUTH_METHODS.NO_AUTH, 'should send NO_AUTH second')\n\n        // Send response selecting USERNAME_PASSWORD\n        socket.write(Buffer.from([0x05, AUTH_METHODS.USERNAME_PASSWORD]))\n        stage = 'auth'\n      } else if (stage === 'auth') {\n        // Parse username/password auth request\n        p.equal(data[0], 0x01, 'should send auth version 1')\n\n        const usernameLen = data[1]\n        const username = data.subarray(2, 2 + usernameLen).toString()\n        p.equal(username, testUsername, 'should send correct username')\n\n        const passwordLen = data[2 + usernameLen]\n        const password = data.subarray(3 + usernameLen, 3 + usernameLen + passwordLen).toString()\n        p.equal(password, testPassword, 'should send correct password')\n\n        // Send auth success response\n        socket.write(Buffer.from([0x01, 0x00]))\n      }\n    })\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, '127.0.0.1', resolve)\n  })\n\n  const { port } = server.address()\n  const socket = net.connect(port, '127.0.0.1')\n\n  await new Promise((resolve) => {\n    socket.on('connect', resolve)\n  })\n\n  const client = new Socks5Client(socket, {\n    username: testUsername,\n    password: testPassword\n  })\n\n  client.on('authenticated', () => {\n    // Test passed\n  })\n\n  await client.handshake()\n\n  // Wait for the authenticated event\n  await new Promise((resolve) => {\n    client.once('authenticated', resolve)\n  })\n\n  socket.destroy()\n  server.close()\n\n  await p.completed\n})\n\ntest('Socks5Client - connect command', async (t) => {\n  const p = tspl(t, { plan: 8 })\n\n  const targetHost = 'example.com'\n  const targetPort = 80\n\n  // Create a mock SOCKS5 server\n  const server = net.createServer((socket) => {\n    let stage = 'handshake'\n\n    socket.on('data', (data) => {\n      if (stage === 'handshake' && data[0] === 0x05) {\n        // Send NO_AUTH response\n        socket.write(Buffer.from([0x05, AUTH_METHODS.NO_AUTH]))\n        stage = 'connect'\n      } else if (stage === 'connect') {\n        // Parse CONNECT request\n        p.equal(data[0], 0x05, 'should send SOCKS version 5')\n        p.equal(data[1], 0x01, 'should send CONNECT command')\n        p.equal(data[2], 0x00, 'should send reserved byte')\n        p.equal(data[3], 0x03, 'should send domain address type')\n\n        const domainLen = data[4]\n        const domain = data.subarray(5, 5 + domainLen).toString()\n        p.equal(domain, targetHost, 'should send correct domain')\n\n        const port = data.readUInt16BE(5 + domainLen)\n        p.equal(port, targetPort, 'should send correct port')\n\n        // Send success response with bound address\n        const response = Buffer.from([\n          0x05, // Version\n          REPLY_CODES.SUCCEEDED, // Success\n          0x00, // Reserved\n          0x01, // IPv4 address type\n          127, 0, 0, 1, // Bound address\n          0x00, 0x50 // Bound port (80)\n        ])\n        socket.write(response)\n      }\n    })\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, '127.0.0.1', resolve)\n  })\n\n  const { port } = server.address()\n  const socket = net.connect(port, '127.0.0.1')\n\n  await new Promise((resolve) => {\n    socket.on('connect', resolve)\n  })\n\n  const client = new Socks5Client(socket)\n\n  client.on('authenticated', async () => {\n    await client.connect(targetHost, targetPort)\n  })\n\n  client.on('connected', (info) => {\n    p.equal(info.address, '127.0.0.1', 'should return bound address')\n    p.equal(info.port, 80, 'should return bound port')\n  })\n\n  await client.handshake()\n\n  // Wait for the connected event\n  await new Promise((resolve) => {\n    client.once('connected', resolve)\n  })\n\n  socket.destroy()\n  server.close()\n\n  await p.completed\n})\n\ntest('Socks5Client - authentication failure', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  // Create a mock SOCKS5 server\n  const server = net.createServer((socket) => {\n    socket.on('data', (data) => {\n      if (data[0] === 0x05) {\n        // Send NO_ACCEPTABLE response\n        socket.write(Buffer.from([0x05, AUTH_METHODS.NO_ACCEPTABLE]))\n      }\n    })\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, '127.0.0.1', resolve)\n  })\n\n  const { port } = server.address()\n  const socket = net.connect(port, '127.0.0.1')\n\n  await new Promise((resolve) => {\n    socket.on('connect', resolve)\n  })\n\n  const client = new Socks5Client(socket)\n\n  client.on('error', (err) => {\n    p.ok(err instanceof Socks5ProxyError, 'should emit Socks5ProxyError')\n    p.equal(err.code, 'UND_ERR_SOCKS5_AUTH_REJECTED', 'should have correct error code')\n    p.equal(err.message, 'No acceptable authentication method', 'should have correct error message')\n  })\n\n  await client.handshake()\n\n  // Wait for the error event\n  await new Promise((resolve) => {\n    client.once('error', resolve)\n  })\n\n  socket.destroy()\n  server.close()\n\n  await p.completed\n})\n\ntest('Socks5Client - connection refused', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  // Create a mock SOCKS5 server\n  const server = net.createServer((socket) => {\n    let stage = 'handshake'\n\n    socket.on('data', (data) => {\n      if (stage === 'handshake' && data[0] === 0x05) {\n        // Send NO_AUTH response\n        socket.write(Buffer.from([0x05, AUTH_METHODS.NO_AUTH]))\n        stage = 'connect'\n      } else if (stage === 'connect') {\n        // Send connection refused response\n        const response = Buffer.from([\n          0x05, // Version\n          REPLY_CODES.CONNECTION_REFUSED, // Connection refused\n          0x00, // Reserved\n          0x01, // IPv4 address type\n          0, 0, 0, 0, // Bound address\n          0x00, 0x00 // Bound port\n        ])\n        socket.write(response)\n      }\n    })\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, '127.0.0.1', resolve)\n  })\n\n  const { port } = server.address()\n  const socket = net.connect(port, '127.0.0.1')\n\n  await new Promise((resolve) => {\n    socket.on('connect', resolve)\n  })\n\n  const client = new Socks5Client(socket)\n\n  client.on('authenticated', () => {\n    client.connect('example.com', 80)\n  })\n\n  client.on('error', (err) => {\n    p.ok(err instanceof Socks5ProxyError, 'should throw Socks5ProxyError')\n    p.equal(err.code, 'UND_ERR_SOCKS5_REPLY_5', 'should have correct error code')\n    p.match(err.message, /Connection refused/, 'should have correct error message')\n  })\n\n  await client.handshake()\n\n  // Wait for the error event\n  await new Promise((resolve) => {\n    client.once('error', resolve)\n  })\n\n  socket.destroy()\n  server.close()\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/socks5-proxy-agent.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst { request } = require('..')\nconst { InvalidArgumentError } = require('../lib/core/errors')\nconst Socks5ProxyAgent = require('../lib/dispatcher/socks5-proxy-agent')\nconst { createServer } = require('node:http')\nconst { TestSocks5Server } = require('./fixtures/socks5-test-server')\n\ntest('Socks5ProxyAgent - constructor validation', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  p.throws(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent()\n  }, InvalidArgumentError, 'should throw when proxy URL is not provided')\n\n  p.throws(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent('http://localhost:1080')\n  }, InvalidArgumentError, 'should throw when proxy URL protocol is not socks5')\n\n  p.doesNotThrow(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent('socks5://localhost:1080')\n  }, 'should accept socks5:// URLs')\n\n  p.doesNotThrow(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent('socks://localhost:1080')\n  }, 'should accept socks:// URLs for compatibility')\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - basic HTTP connection', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  // Create target HTTP server\n  const server = createServer((req, res) => {\n    res.writeHead(200, { 'content-type': 'application/json' })\n    res.end(JSON.stringify({ message: 'Hello from target server', path: req.url }))\n  })\n\n  // Start target server\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n  const serverPort = server.address().port\n\n  // Create SOCKS5 proxy server\n  const socksServer = new TestSocks5Server()\n  const socksAddress = await socksServer.listen()\n\n  try {\n    // Create Socks5ProxyAgent\n    const proxyWrapper = new Socks5ProxyAgent(`socks5://localhost:${socksAddress.port}`)\n\n    // Make request through SOCKS5 proxy\n    const response = await request(`http://localhost:${serverPort}/test`, {\n      dispatcher: proxyWrapper\n    })\n\n    p.equal(response.statusCode, 200, 'should get 200 status code')\n\n    const body = await response.body.json()\n    p.deepEqual(body, {\n      message: 'Hello from target server',\n      path: '/test'\n    }, 'should get correct response body')\n  } finally {\n    await socksServer.close()\n    server.close()\n  }\n\n  await p.completed\n})\n\ntest.skip('Socks5ProxyAgent - HTTPS connection', async (t) => {\n  // Skip HTTPS test for now - TLS option passing needs additional work\n  t.skip('HTTPS test requires TLS option refinement')\n})\n\ntest('Socks5ProxyAgent - with authentication', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  // Create target HTTP server\n  const server = createServer((req, res) => {\n    res.writeHead(200, { 'content-type': 'application/json' })\n    res.end(JSON.stringify({ message: 'Authenticated request successful' }))\n  })\n\n  // Start target server\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n  const serverPort = server.address().port\n\n  // Create SOCKS5 proxy server with auth\n  const socksServer = new TestSocks5Server({\n    requireAuth: true,\n    credentials: { username: 'testuser', password: 'testpass' }\n  })\n  const socksAddress = await socksServer.listen()\n\n  try {\n    // Create Socks5ProxyAgent with auth\n    const proxyWrapper = new Socks5ProxyAgent(`socks5://testuser:testpass@localhost:${socksAddress.port}`)\n\n    // Make request through SOCKS5 proxy\n    const response = await request(`http://localhost:${serverPort}/auth-test`, {\n      dispatcher: proxyWrapper\n    })\n\n    p.equal(response.statusCode, 200, 'should get 200 status code')\n\n    const body = await response.body.json()\n    p.deepEqual(body, {\n      message: 'Authenticated request successful'\n    }, 'should get correct response body')\n  } finally {\n    await socksServer.close()\n    server.close()\n  }\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - authentication with options', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  // Create target HTTP server\n  const server = createServer((req, res) => {\n    res.writeHead(200, { 'content-type': 'application/json' })\n    res.end(JSON.stringify({ message: 'Options auth successful' }))\n  })\n\n  // Start target server\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n  const serverPort = server.address().port\n\n  // Create SOCKS5 proxy server with auth\n  const socksServer = new TestSocks5Server({\n    requireAuth: true,\n    credentials: { username: 'optuser', password: 'optpass' }\n  })\n  const socksAddress = await socksServer.listen()\n\n  try {\n    // Create Socks5ProxyAgent with auth in options\n    const proxyWrapper = new Socks5ProxyAgent(`socks5://localhost:${socksAddress.port}`, {\n      username: 'optuser',\n      password: 'optpass'\n    })\n\n    // Make request through SOCKS5 proxy\n    const response = await request(`http://localhost:${serverPort}/options-auth`, {\n      dispatcher: proxyWrapper\n    })\n\n    p.equal(response.statusCode, 200, 'should get 200 status code')\n\n    const body = await response.body.json()\n    p.deepEqual(body, {\n      message: 'Options auth successful'\n    }, 'should get correct response body')\n  } finally {\n    await socksServer.close()\n    server.close()\n  }\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - multiple requests through same proxy', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  // Create target HTTP server\n  let requestCount = 0\n  const server = createServer((req, res) => {\n    requestCount++\n    res.writeHead(200, { 'content-type': 'application/json' })\n    res.end(JSON.stringify({ message: `Request ${requestCount}`, path: req.url }))\n  })\n\n  // Start target server\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n  const serverPort = server.address().port\n\n  // Create SOCKS5 proxy server\n  const socksServer = new TestSocks5Server()\n  const socksAddress = await socksServer.listen()\n\n  try {\n    // Create Socks5ProxyAgent\n    const proxyWrapper = new Socks5ProxyAgent(`socks5://localhost:${socksAddress.port}`)\n\n    // Make first request\n    const response1 = await request(`http://localhost:${serverPort}/request1`, {\n      dispatcher: proxyWrapper\n    })\n\n    p.equal(response1.statusCode, 200, 'should get 200 status code for first request')\n    const body1 = await response1.body.json()\n    p.deepEqual(body1, { message: 'Request 1', path: '/request1' }, 'should get correct response body for first request')\n\n    // Make second request through same proxy\n    const response2 = await request(`http://localhost:${serverPort}/request2`, {\n      dispatcher: proxyWrapper\n    })\n\n    p.equal(response2.statusCode, 200, 'should get 200 status code for second request')\n    const body2 = await response2.body.json()\n    p.deepEqual(body2, { message: 'Request 2', path: '/request2' }, 'should get correct response body for second request')\n  } finally {\n    await socksServer.close()\n    server.close()\n  }\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - connection failure', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  // Create Socks5ProxyAgent pointing to non-existent proxy\n  const proxyWrapper = new Socks5ProxyAgent('socks5://localhost:9999')\n\n  try {\n    await request('http://example.com/', {\n      dispatcher: proxyWrapper\n    })\n    p.fail('should have thrown an error')\n  } catch (err) {\n    p.ok(err, 'should throw error when SOCKS5 proxy is not available')\n  }\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - proxy connection refused', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  // Create target HTTP server\n  const server = createServer((req, res) => {\n    res.writeHead(200)\n    res.end('OK')\n  })\n\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n  const serverPort = server.address().port\n\n  // Create SOCKS5 proxy server that simulates connection failure\n  const socksServer = new TestSocks5Server({ simulateFailure: true })\n  const socksAddress = await socksServer.listen()\n\n  try {\n    const proxyWrapper = new Socks5ProxyAgent(`socks5://localhost:${socksAddress.port}`)\n\n    await request(`http://localhost:${serverPort}/`, {\n      dispatcher: proxyWrapper\n    })\n    p.fail('should have thrown an error')\n  } catch (err) {\n    p.ok(err, 'should throw error when SOCKS5 proxy refuses connection')\n  } finally {\n    await socksServer.close()\n    server.close()\n  }\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - close and destroy', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const proxyWrapper = new Socks5ProxyAgent('socks5://localhost:1080')\n\n  // Test close\n  await proxyWrapper.close()\n  p.ok(true, 'should close without error')\n\n  // Test destroy\n  await proxyWrapper.destroy()\n  p.ok(true, 'should destroy without error')\n\n  await p.completed\n})\n\ntest('Socks5ProxyAgent - URL parsing edge cases', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  // Test with URL object\n  const url = new URL('socks5://user:pass@proxy.example.com:1080')\n  p.doesNotThrow(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent(url)\n  }, 'should accept URL object')\n\n  // Test with encoded credentials\n  p.doesNotThrow(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent('socks5://user%40domain:p%40ss@localhost:1080')\n  }, 'should handle URL-encoded credentials')\n\n  // Test default port\n  p.doesNotThrow(() => {\n    // eslint-disable-next-line no-new\n    new Socks5ProxyAgent('socks5://localhost')\n  }, 'should use default port 1080')\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/socks5-utils.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test } = require('node:test')\nconst {\n  parseAddress,\n  parseIPv6,\n  buildAddressBuffer,\n  parseResponseAddress,\n  createReplyError\n} = require('../lib/core/socks5-utils')\nconst { InvalidArgumentError } = require('../lib/core/errors')\n\ntest('parseAddress - IPv4', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const result = parseAddress('192.168.1.1')\n  p.equal(result.type, 0x01, 'should return IPv4 type')\n  p.equal(result.buffer.length, 4, 'should return 4-byte buffer')\n  p.deepEqual(Array.from(result.buffer), [192, 168, 1, 1], 'should parse IPv4 correctly')\n\n  await p.completed\n})\n\ntest('parseAddress - IPv6', async (t) => {\n  const p = tspl(t, { plan: 2 })\n\n  const result = parseAddress('2001:db8::1')\n  p.equal(result.type, 0x04, 'should return IPv6 type')\n  p.equal(result.buffer.length, 16, 'should return 16-byte buffer')\n\n  await p.completed\n})\n\ntest('parseAddress - Domain', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  const result = parseAddress('example.com')\n  p.equal(result.type, 0x03, 'should return domain type')\n  p.equal(result.buffer[0], 11, 'should have correct length byte')\n  p.equal(result.buffer.subarray(1).toString(), 'example.com', 'should contain domain name')\n\n  // Test domain too long\n  const longDomain = 'a'.repeat(256)\n  p.throws(() => parseAddress(longDomain), InvalidArgumentError, 'should throw for domain > 255 bytes')\n\n  await p.completed\n})\n\ntest('parseIPv6', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  // Test full IPv6\n  const buffer1 = parseIPv6('2001:0db8:0000:0042:0000:8a2e:0370:7334')\n  p.equal(buffer1.length, 16, 'should return 16-byte buffer')\n\n  // Test compressed IPv6\n  const buffer2 = parseIPv6('2001:db8::1')\n  p.equal(buffer2.length, 16, 'should return 16-byte buffer for compressed')\n\n  // Test loopback\n  const buffer3 = parseIPv6('::1')\n  p.equal(buffer3.length, 16, 'should return 16-byte buffer for loopback')\n\n  await p.completed\n})\n\ntest('buildAddressBuffer', async (t) => {\n  const p = tspl(t, { plan: 5 })\n\n  // IPv4 address\n  const ipv4Buffer = buildAddressBuffer(0x01, Buffer.from([192, 168, 1, 1]), 80)\n  p.equal(ipv4Buffer[0], 0x01, 'should have IPv4 type')\n  p.deepEqual(Array.from(ipv4Buffer.subarray(1, 5)), [192, 168, 1, 1], 'should have IPv4 address')\n  p.equal(ipv4Buffer.readUInt16BE(5), 80, 'should have correct port')\n\n  // Domain address\n  const domainBuffer = Buffer.concat([Buffer.from([11]), Buffer.from('example.com')])\n  const result = buildAddressBuffer(0x03, domainBuffer, 443)\n  p.equal(result[0], 0x03, 'should have domain type')\n  p.equal(result.readUInt16BE(result.length - 2), 443, 'should have correct port')\n\n  await p.completed\n})\n\ntest('parseResponseAddress - IPv4', async (t) => {\n  const p = tspl(t, { plan: 4 })\n\n  const buffer = Buffer.from([\n    0x01, // IPv4 type\n    192, 168, 1, 1, // IP address\n    0x00, 0x50 // Port 80\n  ])\n\n  const result = parseResponseAddress(buffer)\n  p.equal(result.address, '192.168.1.1', 'should parse IPv4 address')\n  p.equal(result.port, 80, 'should parse port')\n  p.equal(result.bytesRead, 7, 'should read 7 bytes')\n\n  // Test with offset\n  const bufferWithOffset = Buffer.concat([Buffer.from([0, 0]), buffer])\n  const resultWithOffset = parseResponseAddress(bufferWithOffset, 2)\n  p.equal(resultWithOffset.address, '192.168.1.1', 'should parse with offset')\n\n  await p.completed\n})\n\ntest('parseResponseAddress - Domain', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const buffer = Buffer.from([\n    0x03, // Domain type\n    11, // Length\n    ...Buffer.from('example.com'),\n    0x01, 0xBB // Port 443\n  ])\n\n  const result = parseResponseAddress(buffer)\n  p.equal(result.address, 'example.com', 'should parse domain')\n  p.equal(result.port, 443, 'should parse port')\n  p.equal(result.bytesRead, 15, 'should read correct bytes')\n\n  await p.completed\n})\n\ntest('parseResponseAddress - IPv6', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const buffer = Buffer.alloc(19)\n  buffer[0] = 0x04 // IPv6 type\n  // Simple IPv6 address (all zeros except last byte)\n  buffer[17] = 1\n  buffer[17] = 0x00\n  buffer[18] = 0x50 // Port 80\n\n  const result = parseResponseAddress(buffer)\n  p.match(result.address, /:/, 'should return IPv6 format')\n  p.equal(result.port, 80, 'should parse port')\n  p.equal(result.bytesRead, 19, 'should read 19 bytes')\n\n  await p.completed\n})\n\ntest('parseResponseAddress - errors', async (t) => {\n  const p = tspl(t, { plan: 5 })\n\n  // Buffer too small for type\n  p.throws(() => parseResponseAddress(Buffer.alloc(0)), InvalidArgumentError)\n\n  // Buffer too small for IPv4\n  p.throws(() => parseResponseAddress(Buffer.from([0x01, 192])), InvalidArgumentError)\n\n  // Buffer too small for domain length\n  p.throws(() => parseResponseAddress(Buffer.from([0x03])), InvalidArgumentError)\n\n  // Buffer too small for domain\n  p.throws(() => parseResponseAddress(Buffer.from([0x03, 10, 65])), InvalidArgumentError)\n\n  // Invalid address type\n  p.throws(() => parseResponseAddress(Buffer.from([0x99, 0, 0, 0, 0, 0, 0])), InvalidArgumentError)\n\n  await p.completed\n})\n\ntest('createReplyError', async (t) => {\n  const p = tspl(t, { plan: 6 })\n\n  const err1 = createReplyError(0x01)\n  p.equal(err1.message, 'General SOCKS server failure')\n  p.equal(err1.code, 'SOCKS5_1')\n\n  const err2 = createReplyError(0x05)\n  p.equal(err2.message, 'Connection refused')\n  p.equal(err2.code, 'SOCKS5_5')\n\n  const err3 = createReplyError(0x99)\n  p.equal(err3.message, 'Unknown SOCKS5 error code: 153')\n  p.equal(err3.code, 'SOCKS5_153')\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/stream-compat.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\nconst { Readable } = require('node:stream')\nconst EE = require('node:events')\n\ntest('stream body without destroy', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    const signal = new EE()\n    const body = new Readable({ read () {} })\n    body.destroy = undefined\n    body.on('error', (err) => {\n      t.ok(err)\n    })\n    client.request({\n      path: '/',\n      method: 'PUT',\n      signal,\n      body\n    }, (err, data) => {\n      t.ok(err)\n    })\n    signal.emit('abort')\n  })\n\n  await t.completed\n})\n\ntest('IncomingMessage', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.end()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const proxyClient = new Client(`http://localhost:${server.address().port}`)\n    after(() => proxyClient.destroy())\n\n    proxyClient.on('disconnect', () => {\n      if (!proxyClient.closed && !proxyClient.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const proxy = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      proxyClient.request({\n        path: '/',\n        method: 'PUT',\n        body: req\n      }, (err, data) => {\n        t.ifError(err)\n        data.body.pipe(res)\n      })\n    })\n    after(() => proxy.close())\n\n    proxy.listen(0, () => {\n      const client = new Client(`http://localhost:${proxy.address().port}`)\n      after(() => client.destroy())\n\n      client.on('disconnect', () => {\n        if (!client.closed && !client.destroyed) {\n          t.fail('unexpected disconnect')\n        }\n      })\n\n      client.request({\n        path: '/',\n        method: 'PUT',\n        body: 'hello world'\n      }, (err, data) => {\n        t.ifError(err)\n      })\n    })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/subresource-integrity/apply-algorithm-to-bytes.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\n\nconst { applyAlgorithmToBytes } = require('../../lib/web/subresource-integrity/subresource-integrity')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\n\nconst skip = runtimeFeatures.has('crypto') === false\n\ndescribe('applyAlgorithmToBytes', () => {\n  /* Hash values generated with for \"Hello world!\" */\n  const hash256 = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n  const hash384 = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY/JV615lypu7hkHMz+'\n  const hash512 = '9s3ioPgZMUzd5V/CJ9jX2uPSjMVWIioKitZtkcytSq1glPUXohgjYMmqz2o9wyMWLLb9jN/+2w/gOPVehf+1tg=='\n\n  test('valid sha256', { skip }, (t) => {\n    const result = applyAlgorithmToBytes('sha256', Buffer.from('Hello world!'))\n    t.assert.strictEqual(result, hash256)\n  })\n  test('valid sha384', { skip }, (t) => {\n    const result = applyAlgorithmToBytes('sha384', Buffer.from('Hello world!'))\n    t.assert.strictEqual(result, hash384)\n  })\n  test('valid sha512', { skip }, (t) => {\n    const result = applyAlgorithmToBytes('sha512', Buffer.from('Hello world!'))\n    t.assert.strictEqual(result, hash512)\n  })\n})\n"
  },
  {
    "path": "test/subresource-integrity/bytes-match.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\n\nconst { bytesMatch } = require('../../lib/web/subresource-integrity/subresource-integrity')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\n\nconst skip = runtimeFeatures.has('crypto') === false\n\ndescribe('bytesMatch', () => {\n  test('valid sha256 and base64', { skip }, (t) => {\n    const data = Buffer.from('Hello world!')\n    const hash256 = 'sha256-wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n    t.assert.ok(bytesMatch(data, hash256))\n  })\n})\n"
  },
  {
    "path": "test/subresource-integrity/case-sensitive-match.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\n\nconst { caseSensitiveMatch } = require('../../lib/web/subresource-integrity/subresource-integrity')\n\ndescribe('caseSensitiveMatch', () => {\n  test('identical strings', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, actualValue has one padding char', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, expectedValue has one padding char', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, expectedValue has two padding chars', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs=='\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, both have one padding char', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs='\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, both have two padding chars', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs=='\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs=='\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('identical strings, expectedValue has invalid third padding char', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs=='\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs==='\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue) === false)\n  })\n\n  test('expectedValue can be base64Url - match `_`', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha/uSLs'\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('expectedValue can be base64Url - match `+`', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7+gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7-gUfE5yuYB3ha/uSLs'\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n\n  test('should be case sensitive', (t) => {\n    const actualValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs'\n    const expectedValue = 'ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLS'\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue) === false)\n  })\n\n  test('empty string should return true', (t) => {\n    const actualValue = ''\n    const expectedValue = ''\n    t.assert.ok(caseSensitiveMatch(actualValue, expectedValue))\n  })\n})\n"
  },
  {
    "path": "test/subresource-integrity/get-strongest-metadata.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\n\nconst { getStrongestMetadata } = require('../../lib/web/subresource-integrity/subresource-integrity')\n\ndescribe('getStrongestMetadata', () => {\n  test('should return strongest sha512 /1', (t) => {\n    const result = getStrongestMetadata([\n      { alg: 'sha256', val: 'sha256-abc' },\n      { alg: 'sha384', val: 'sha384-def' },\n      { alg: 'sha512', val: 'sha512-ghi' }\n    ])\n    t.assert.deepEqual(result, [\n      { alg: 'sha512', val: 'sha512-ghi' }\n    ])\n  })\n\n  test('should return strongest sha512 /2', (t) => {\n    const result = getStrongestMetadata([\n      { alg: 'sha512', val: 'sha512-ghi' },\n      { alg: 'sha256', val: 'sha256-abc' },\n      { alg: 'sha384', val: 'sha384-def' }\n    ])\n    t.assert.deepEqual(result, [\n      { alg: 'sha512', val: 'sha512-ghi' }\n    ])\n  })\n\n  test('should return strongest sha384', (t) => {\n    const result = getStrongestMetadata([\n      { alg: 'sha256', val: 'sha256-abc' },\n      { alg: 'sha384', val: 'sha384-def' }\n    ])\n    t.assert.deepEqual(result, [\n      { alg: 'sha384', val: 'sha384-def' }\n    ])\n  })\n\n  test('should return both strongest sha384', (t) => {\n    const result = getStrongestMetadata([\n      { alg: 'sha384', val: 'sha384-abc' },\n      { alg: 'sha256', val: 'sha256-def' },\n      { alg: 'sha384', val: 'sha384-ghi' }\n    ])\n    t.assert.deepEqual(result, [\n      { alg: 'sha384', val: 'sha384-abc' },\n      { alg: 'sha384', val: 'sha384-ghi' }\n    ])\n  })\n\n  test('should return multiple metadata with the same strength', (t) => {\n    const result = getStrongestMetadata([\n      { alg: 'sha256', val: 'sha256-abc' }\n    ])\n    t.assert.deepEqual(result, [\n      { alg: 'sha256', val: 'sha256-abc' }\n    ])\n  })\n\n  test('should return empty array when no metadata is provided', (t) => {\n    const result = getStrongestMetadata([])\n    t.assert.deepEqual(result, [])\n  })\n\n  test('should throw when invalid hash algorithm is provided', (t) => {\n    t.assert.throws(() => getStrongestMetadata([\n      { alg: 'sha1024', val: 'sha1024-xyz' }\n    ]), {\n      name: 'AssertionError',\n      message: 'Invalid SRI hash algorithm token'\n    })\n  })\n})\n"
  },
  {
    "path": "test/subresource-integrity/is-valid-sri-hash-algorithm.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features.js')\n\nconst { isValidSRIHashAlgorithm } = require('../../lib/web/subresource-integrity/subresource-integrity')\n\nconst skip = runtimeFeatures.has('crypto') === false\n\ndescribe('isValidSRIHashAlgorithm', () => {\n  test('valid sha256', { skip }, (t) => {\n    t.assert.ok(isValidSRIHashAlgorithm('sha256'))\n  })\n  test('valid sha384', { skip }, (t) => {\n    t.assert.ok(isValidSRIHashAlgorithm('sha384'))\n  })\n  test('valid sha512', { skip }, (t) => {\n    t.assert.ok(isValidSRIHashAlgorithm('sha512'))\n  })\n  test('invalid sha1024', (t) => {\n    t.assert.ok(isValidSRIHashAlgorithm('sha1024') === false)\n  })\n})\n"
  },
  {
    "path": "test/subresource-integrity/parse-metadata.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\n\nconst { parseMetadata } = require('../../lib/web/subresource-integrity/subresource-integrity')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\n\nconst skip = runtimeFeatures.has('crypto') === false\n\ndescribe('parseMetadata', () => {\n  /* Hash values generated with for \"Hello world!\" */\n  const hash256 = 'wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro='\n  const hash384 = 'hiVfosNuSzCWnq4X3DTHcsvr38WLWEA5AL6HYU6xo0uHgCY/JV615lypu7hkHMz+'\n  const hash512 = '9s3ioPgZMUzd5V/CJ9jX2uPSjMVWIioKitZtkcytSq1glPUXohgjYMmqz2o9wyMWLLb9jN/+2w/gOPVehf+1tg=='\n\n  test('should parse valid metadata with option', { skip }, (t) => {\n    const validMetadata = `sha256-${hash256} !@ sha384-${hash384} !@ sha512-${hash512} !@`\n    const result = parseMetadata(validMetadata)\n\n    t.assert.deepEqual(result, [\n      { alg: 'sha256', val: hash256 },\n      { alg: 'sha384', val: hash384 },\n      { alg: 'sha512', val: hash512 }\n    ])\n  })\n\n  test('should parse valid metadata with non ASCII chars option', { skip }, (t) => {\n    const validMetadata = `sha256-${hash256} !© sha384-${hash384} !€ sha512-${hash512} !µ`\n    const result = parseMetadata(validMetadata)\n\n    t.assert.deepEqual(result, [\n      { alg: 'sha256', val: hash256 },\n      { alg: 'sha384', val: hash384 },\n      { alg: 'sha512', val: hash512 }\n    ])\n  })\n\n  test('should parse valid metadata without option', { skip }, (t) => {\n    const validMetadata = `sha256-${hash256} sha384-${hash384} sha512-${hash512}`\n    const result = parseMetadata(validMetadata)\n\n    t.assert.deepEqual(result, [\n      { alg: 'sha256', val: hash256 },\n      { alg: 'sha384', val: hash384 },\n      { alg: 'sha512', val: hash512 }\n    ])\n  })\n\n  test('should not set hash as undefined when invalid base64 chars are provided', { skip }, (t) => {\n    const invalidHash384 = 'zifp5hE1Xl5LQQqQz[]Bq/iaq9Wb6jVb//T7EfTmbXD2aEP5c2ZdJr9YTDfcTE1ZH+'\n\n    const validMetadata = `sha256-${hash256} sha384-${invalidHash384} sha512-${hash512}`\n    const result = parseMetadata(validMetadata)\n\n    t.assert.deepEqual(result, [\n      { alg: 'sha256', val: hash256 },\n      { alg: 'sha384', val: invalidHash384 },\n      { alg: 'sha512', val: hash512 }\n    ])\n  })\n})\n"
  },
  {
    "path": "test/sync-error-in-callback.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('synchronous error in request callback should reach uncaughtException handler', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = createServer((req, res) => {\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const testError = new Error('sync error in callback')\n\n  // Set up uncaughtException handler\n  const originalHandler = process.listenerCount('uncaughtException') > 0\n    ? process.listeners('uncaughtException')[0]\n    : null\n\n  process.removeAllListeners('uncaughtException')\n\n  const uncaughtHandler = (err) => {\n    p.strictEqual(err, testError, 'Error should reach uncaughtException handler')\n    // Clean up\n    process.removeListener('uncaughtException', uncaughtHandler)\n    if (originalHandler) {\n      process.on('uncaughtException', originalHandler)\n    }\n  }\n\n  process.on('uncaughtException', uncaughtHandler)\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    p.ifError(err)\n    p.strictEqual(data.statusCode, 200)\n\n    // Destroy the stream to simulate the described scenario\n    data.body.destroy()\n\n    // This synchronous error should reach the uncaughtException handler\n    throw testError\n  })\n\n  // Wait a bit to ensure the uncaughtException handler is triggered\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  // Clean up handler if not triggered\n  process.removeListener('uncaughtException', uncaughtHandler)\n  if (originalHandler) {\n    process.on('uncaughtException', originalHandler)\n  }\n\n  await p.completed\n})\n\ntest('synchronous error thrown immediately in request callback', async (t) => {\n  const p = tspl(t, { plan: 3 })\n\n  const server = createServer((req, res) => {\n    res.end('hello')\n  })\n  after(() => server.close())\n\n  await new Promise((resolve) => {\n    server.listen(0, resolve)\n  })\n\n  const client = new Client(`http://localhost:${server.address().port}`)\n  after(() => client.close())\n\n  const testError = new Error('immediate sync error')\n\n  // Set up uncaughtException handler\n  const originalHandler = process.listenerCount('uncaughtException') > 0\n    ? process.listeners('uncaughtException')[0]\n    : null\n\n  process.removeAllListeners('uncaughtException')\n\n  const uncaughtHandler = (err) => {\n    p.strictEqual(err, testError, 'Error should reach uncaughtException handler')\n    // Clean up\n    process.removeListener('uncaughtException', uncaughtHandler)\n    if (originalHandler) {\n      process.on('uncaughtException', originalHandler)\n    }\n  }\n\n  process.on('uncaughtException', uncaughtHandler)\n\n  client.request({\n    path: '/',\n    method: 'GET'\n  }, (err, data) => {\n    p.ifError(err)\n    p.strictEqual(data.statusCode, 200)\n\n    // Throw immediately without any stream operations\n    throw testError\n  })\n\n  // Wait a bit to ensure the uncaughtException handler is triggered\n  await new Promise(resolve => setTimeout(resolve, 100))\n\n  // Clean up handler if not triggered\n  process.removeListener('uncaughtException', uncaughtHandler)\n  if (originalHandler) {\n    process.on('uncaughtException', originalHandler)\n  }\n\n  await p.completed\n})\n\ntest('synchronous error in request callback with error parameter', async (t) => {\n  const p = tspl(t, { plan: 1 })\n\n  const server = createServer((req, res) => {\n    // Force an error by destroying the socket\n    req.socket.destroy()\n  })\n  after(() => server.close())\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET'\n    }, (err, data) => {\n      // We expect an error from the destroyed socket\n      p.ok(err)\n\n      // Don't throw here as it would interfere with test completion\n      // The important tests are the ones where we get successful responses\n    })\n  })\n\n  await p.completed\n})\n"
  },
  {
    "path": "test/timers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test } = require('node:test')\nconst FakeTimers = require('@sinonjs/fake-timers')\n\nconst clock = FakeTimers.install()\n\nconst timers = require('../lib/util/timers')\nconst { eventLoopBlocker } = require('./utils/event-loop-blocker')\n\n// timers.setTimeout implements a low resolution timer with a 500 ms granularity\n// It is expected that in the worst case, a timer will fire about 500 ms after the\n// intended amount of time, an extra 200 ms is added to account event loop overhead\n// Timers should never fire excessively early, 1ms early is tolerated\nconst ACCEPTABLE_DELTA = 700\n\nfunction tick (duration) {\n  for (let i = 0; i < duration; ++i) {\n    clock.tick(1)\n  }\n}\n\ndescribe('timers', () => {\n  test('timers exports a clearTimeout', (t) => {\n    t = tspl(t, { plan: 1 })\n\n    t.ok(typeof timers.clearTimeout === 'function')\n  })\n\n  test('timers exports a setTimeout', (t) => {\n    t = tspl(t, { plan: 1 })\n\n    t.ok(typeof timers.setTimeout === 'function')\n  })\n\n  test('setTimeout instantiates a native NodeJS.Timeout when delay is lower or equal 1e3 ms', (t) => {\n    t = tspl(t, { plan: 2 })\n\n    t.strictEqual(timers.setTimeout(() => { }, 999)[timers.kFastTimer], undefined)\n    t.strictEqual(timers.setTimeout(() => { }, 1e3)[timers.kFastTimer], undefined)\n  })\n\n  test('setTimeout instantiates a FastTimer when delay is bigger than 1e3 ms', (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const timeout = timers.setTimeout(() => { }, 1001)\n    t.strictEqual(timeout[timers.kFastTimer], true)\n  })\n\n  test('clearTimeout can clear a node native Timeout', (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const nativeTimeoutId = setTimeout(() => { t.fail() }, 1)\n    t.ok(timers.clearTimeout(nativeTimeoutId) === undefined)\n    tick(10)\n  })\n\n  test('a FastTimer will get a _idleStart value after short time', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const timer = timers.setTimeout(() => {\n      t.fail('timer should not have fired')\n    }, 1e4)\n\n    t.strictEqual(timer[timers.kFastTimer], true)\n    t.strictEqual(timer._idleStart, -1)\n\n    tick(1e3)\n    t.notStrictEqual(timer._idleStart, -1)\n\n    timers.clearTimeout(timer)\n  })\n\n  test('a cleared FastTimer will reset the _idleStart value to -1', async (t) => {\n    t = tspl(t, { plan: 4 })\n\n    const timer = timers.setTimeout(() => {\n      t.fail('timer should not have fired')\n    }, 1e4)\n\n    t.strictEqual(timer[timers.kFastTimer], true)\n    t.strictEqual(timer._idleStart, -1)\n    tick(750)\n    t.notStrictEqual(timer._idleStart, -1)\n    timers.clearTimeout(timer)\n    t.strictEqual(timer._idleStart, -1)\n  })\n\n  test('a FastTimer can be cleared', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const timer = timers.setTimeout(() => {\n      t.fail('timer should not have fired')\n    }, 1001)\n\n    t.strictEqual(timer[timers.kFastTimer], true)\n    timers.clearTimeout(timer)\n\n    t.strictEqual(timer._idleStart, -1)\n    tick(750)\n    t.strictEqual(timer._idleStart, -1)\n  })\n\n  test('a cleared FastTimer can be refreshed', async (t) => {\n    t = tspl(t, { plan: 2 })\n\n    const timer = timers.setFastTimeout(() => {\n      t.ok('pass')\n    }, 1001)\n\n    t.strictEqual(timer[timers.kFastTimer], true)\n    timers.clearTimeout(timer)\n    timer.refresh()\n    tick(2000)\n    timers.clearTimeout(timer)\n  })\n\n  const getDelta = (start, target) => {\n    const end = performance.now()\n    const actual = end - start\n    return actual - target\n  }\n\n  test('refresh correctly with timeout < TICK_MS', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const start = performance.now()\n\n    const timeout = timers.setTimeout(() => {\n      // 80 ms timer was refreshed after 120 ms; total target is 200 ms\n      const delta = getDelta(start, 200)\n\n      t.ok(delta >= -1, 'refreshed timer fired early')\n      t.ok(delta < ACCEPTABLE_DELTA, 'refreshed timer fired late')\n    }, 80)\n\n    setTimeout(() => timeout.refresh(), 40)\n    setTimeout(() => timeout.refresh(), 80)\n    setTimeout(() => timeout.refresh(), 120)\n\n    setTimeout(() => t.ok(true), 260)\n\n    tick(500)\n    await t.completed\n  })\n\n  test('refresh correctly with timeout > TICK_MS', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    const start = performance.now()\n\n    const timeout = timers.setTimeout(() => {\n      // 501ms timer was refreshed after 1250ms; total target is 1751\n      const delta = getDelta(start, 1751)\n\n      t.ok(delta >= -1, 'refreshed timer fired early')\n      t.ok(delta < ACCEPTABLE_DELTA, 'refreshed timer fired late')\n    }, 501)\n\n    setTimeout(() => timeout.refresh(), 250)\n    setTimeout(() => timeout.refresh(), 750)\n    setTimeout(() => timeout.refresh(), 1250)\n\n    setTimeout(() => t.ok(true), 1800)\n\n    tick(2000)\n    await t.completed\n  })\n\n  test('refresh correctly FastTimer with timeout > TICK_MS', async (t) => {\n    t = tspl(t, { plan: 3 })\n\n    // The long running FastTimer will ensure that the internal clock is\n    // incremented by the TICK_MS value in the onTick function\n    const longRunningFastTimer = timers.setTimeout(() => {}, 1e10)\n\n    const start = timers.now()\n\n    const timeout = timers.setFastTimeout(() => {\n      const delta = (timers.now() - start) - 2493\n\n      t.ok(delta >= -1, `refreshed timer fired early (${delta} ms)`)\n      t.ok(delta < ACCEPTABLE_DELTA, `refreshed timer fired late (${delta} ms)`)\n    }, 1001)\n\n    tick(250)\n    timeout.refresh()\n\n    tick(250)\n    timeout.refresh()\n\n    tick(250)\n    timeout.refresh()\n\n    tick(250)\n    timeout.refresh()\n\n    timers.clearTimeout(longRunningFastTimer)\n    setTimeout(() => t.ok(true), 500)\n\n    tick(5000)\n    await t.completed\n  })\n\n  test('a FastTimer will only increment by the defined TICK_MS value', async (t) => {\n    t = tspl(t, { plan: 6 })\n\n    const startInternalClock = timers.now()\n\n    // The long running FastTimer will ensure that the internal clock is\n    // incremented by the TICK_MS value in the onTick function\n    const longRunningFastTimer = timers.setTimeout(() => {}, 1e10)\n\n    eventLoopBlocker(1000)\n\n    // wait to ensure the timer has fired in the next loop\n    await new Promise((resolve) => resolve())\n\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 0)\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 499)\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 499)\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 998)\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 998)\n    tick(250)\n    t.strictEqual(timers.now() - startInternalClock, 1497)\n\n    timers.clearTimeout(longRunningFastTimer)\n  })\n\n  test('meet acceptable resolution time', async (t) => {\n    const testTimeouts = [0, 1, 499, 500, 501, 990, 999, 1000, 1001, 1100, 1400, 1499, 1500, 4000, 5000]\n\n    t = tspl(t, { plan: testTimeouts.length * 2 })\n\n    const start = performance.now()\n\n    for (const target of testTimeouts) {\n      timers.setTimeout(() => {\n        const delta = getDelta(start, target)\n\n        t.ok(delta >= -1, `${target}ms fired early`)\n        t.ok(delta < ACCEPTABLE_DELTA, `${target}ms fired late, got difference of ${delta}ms`)\n      }, target)\n    }\n\n    for (let i = 0; i < 6000; ++i) {\n      clock.tick(1)\n    }\n\n    await t.completed\n  })\n})\n"
  },
  {
    "path": "test/tls-cert-leak.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { tspl } = require('@matteo.collina/tspl')\nconst { fetch } = require('..')\nconst https = require('node:https')\nconst fs = require('node:fs')\nconst path = require('node:path')\nconst { closeServerAsPromise } = require('./utils/node-http')\n\nconst hasGC = typeof global.gc !== 'undefined'\n\n// This test verifies that there is no memory leak when handling TLS certificate errors.\n// It simulates the error by using a server with a self-signed certificate.\ntest('no memory leak with TLS certificate errors', { timeout: 20000 }, async (t) => {\n  if (!hasGC) {\n    throw new Error('gc is not available. Run with \\'--expose-gc\\'.')\n  }\n\n  const { ok } = tspl(t, { plan: 1 })\n\n  // Create HTTPS server with self-signed certificate\n  const serverOptions = {\n    key: fs.readFileSync(path.join(__dirname, 'fixtures', 'key.pem')),\n    cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'cert.pem')),\n    joinDuplicateHeaders: true\n  }\n\n  // Create a server that always responds with a simple message\n  const server = https.createServer(serverOptions, (req, res) => {\n    res.writeHead(200)\n    res.end('test response')\n  })\n\n  // Start server on a random port\n  await new Promise(resolve => server.listen(0, resolve))\n  const serverUrl = `https://localhost:${server.address().port}`\n\n  t.after(closeServerAsPromise(server))\n\n  // Function to make a request that will trigger a certificate error\n  async function makeRequest (i) {\n    try {\n      // The request will fail with CERT_SIGNATURE_FAILURE or similar\n      // because we're using a self-signed certificate and not telling\n      // Node.js to accept it\n      const res = await fetch(`${serverUrl}/request-${i}`, {\n        signal: AbortSignal.timeout(2000) // Short timeout to prevent hanging\n      })\n      const text = await res.text()\n      return { status: res.status, text }\n    } catch (e) {\n      // In real code, without the fix, this would leak memory\n      if (e?.cause?.code === 'CERT_SIGNATURE_FAILURE' ||\n          e?.cause?.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' ||\n          e?.cause?.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {\n        return { status: 524, text: 'Certificate Error' }\n      }\n      // Return for any other error to avoid test interruption\n      return { status: 500, text: e.message }\n    }\n  }\n\n  // Counter for completed requests\n  let complete = 0\n  const requestCount = 400\n\n  // Track memory usage\n  const measurements = []\n  let baselineMemory = 0\n\n  // Process a batch of requests\n  async function processBatch (start, batchSize) {\n    const promises = []\n    const end = Math.min(start + batchSize, requestCount)\n\n    for (let i = start; i < end; i++) {\n      promises.push(makeRequest(i))\n    }\n\n    await Promise.all(promises)\n    complete += promises.length\n\n    // Measure memory after each batch\n    if (complete % 50 === 0 || complete === end) {\n      // Run GC multiple times to get more stable readings\n      global.gc()\n      await new Promise(resolve => setTimeout(resolve, 50))\n      global.gc()\n\n      const memUsage = process.memoryUsage()\n\n      // Establish baseline after first batch\n      if (measurements.length === 0) {\n        baselineMemory = memUsage.heapUsed\n      }\n\n      measurements.push({\n        complete,\n        heapUsed: memUsage.heapUsed\n      })\n\n      console.log(`Completed ${complete}/${requestCount}: Heap: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`)\n\n      // Check memory trend after we have enough data\n      if (measurements.length >= 4) {\n        const hasLeak = checkMemoryTrend()\n        if (hasLeak) {\n          return true // Indicates a leak was detected\n        }\n      }\n    }\n\n    return false // No leak detected\n  }\n\n  // Main test logic\n  async function runTest () {\n    const batchSize = 50\n\n    for (let i = 0; i < requestCount; i += batchSize) {\n      const leakDetected = await processBatch(i, batchSize)\n      if (leakDetected) {\n        // If a leak is detected, fail the test\n        assert.fail('Memory leak detected: heap usage is consistently increasing at a significant rate')\n        return\n      }\n\n      // Check if we have sufficient measurements or have done 350 requests\n      if (measurements.length >= 7 || complete >= 350) {\n        break\n      }\n    }\n\n    // Final check\n    const finalCheckResult = finalMemoryCheck()\n    if (finalCheckResult) {\n      assert.fail(`Memory leak detected: ${finalCheckResult}`)\n    } else {\n      ok(true, 'Memory usage has stabilized')\n    }\n  }\n\n  // Check if memory usage has a concerning trend\n  function checkMemoryTrend () {\n    // Calculate memory growth between each measurement\n    const growthRates = []\n    for (let i = 1; i < measurements.length; i++) {\n      const prev = measurements[i - 1].heapUsed\n      const current = measurements[i].heapUsed\n      growthRates.push((current - prev) / prev)\n    }\n\n    // Calculate growth from baseline\n    const totalGrowthFromBaseline = (measurements[measurements.length - 1].heapUsed - baselineMemory) / baselineMemory\n\n    // Calculate average growth rate\n    const avgGrowthRate = growthRates.reduce((sum, rate) => sum + rate, 0) / growthRates.length\n\n    console.log(`Growth from baseline: ${(totalGrowthFromBaseline * 100).toFixed(2)}%`)\n    console.log(`Average growth rate: ${(avgGrowthRate * 100).toFixed(2)}%`)\n    console.log(`Growth rates: ${growthRates.map(r => (r * 100).toFixed(2) + '%').join(', ')}`)\n\n    // Only flag as leak if all conditions are met:\n    // 1. Consistent growth (majority of measurements show growth)\n    // 2. Average growth rate is significant (>2%)\n    // 3. Total growth from baseline is significant (>20%)\n\n    // Count how many positive growth rates we have\n    const positiveGrowthRates = growthRates.filter(rate => rate > 0.01).length\n\n    return (\n      positiveGrowthRates >= Math.ceil(growthRates.length * 0.75) && // 75% of measurements show growth >1%\n      avgGrowthRate > 0.02 && // Average growth >2%\n      totalGrowthFromBaseline > 0.2 // Total growth >20%\n    )\n  }\n\n  // Final memory check with adjusted requirements\n  function finalMemoryCheck () {\n    if (measurements.length < 4) return false\n\n    // Calculate growth from baseline to the last measurement\n    const totalGrowthFromBaseline = (measurements[measurements.length - 1].heapUsed - baselineMemory) / baselineMemory\n    console.log(`Final growth from baseline: ${(totalGrowthFromBaseline * 100).toFixed(2)}%`)\n\n    // Calculate final slope over the last 150 requests\n    const lastMeasurements = measurements.slice(-3)\n    const finalSlope = (lastMeasurements[2].heapUsed - lastMeasurements[0].heapUsed) /\n                      (lastMeasurements[2].complete - lastMeasurements[0].complete)\n\n    console.log(`Final memory slope: ${finalSlope.toFixed(2)} bytes per request`)\n\n    // Only consider it a leak if:\n    // 1. Total growth is very significant (>25%)\n    if (totalGrowthFromBaseline > 0.25) {\n      return `Excessive memory growth of ${(totalGrowthFromBaseline * 100).toFixed(2)}%`\n    }\n\n    // 2. Memory is still growing rapidly at the end (>2000 bytes per request)\n    if (finalSlope > 2000) {\n      return `Memory still growing rapidly at ${finalSlope.toFixed(2)} bytes per request`\n    }\n\n    return false\n  }\n\n  await runTest()\n})\n"
  },
  {
    "path": "test/tls-session-reuse.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after, describe } = require('node:test')\nconst { readFileSync } = require('node:fs')\nconst { join } = require('node:path')\nconst https = require('node:https')\nconst crypto = require('node:crypto')\nconst { Client, Pool } = require('..')\nconst { kSocket } = require('../lib/core/symbols')\n\nconst options = {\n  key: readFileSync(join(__dirname, 'fixtures', 'key.pem'), 'utf8'),\n  cert: readFileSync(join(__dirname, 'fixtures', 'cert.pem'), 'utf8'),\n  joinDuplicateHeaders: true\n}\nconst ca = readFileSync(join(__dirname, 'fixtures', 'ca.pem'), 'utf8')\n\ndescribe('A client should disable session caching', () => {\n  const clientSessions = {}\n  let serverRequests = 0\n\n  test('Prepare request', async t => {\n    t = tspl(t, { plan: 3 })\n    const server = https.createServer(options, (req, res) => {\n      if (req.url === '/drop-key') {\n        server.setTicketKeys(crypto.randomBytes(48))\n      }\n      serverRequests++\n      res.end()\n    })\n\n    server.listen(0, function () {\n      const tls = {\n        ca,\n        rejectUnauthorized: false,\n        servername: 'agent1'\n      }\n      const client = new Client(`https://localhost:${server.address().port}`, {\n        pipelining: 0,\n        tls,\n        maxCachedSessions: 0\n      })\n\n      after(() => {\n        client.close()\n        server.close()\n      })\n\n      const queue = [{\n        name: 'first',\n        method: 'GET',\n        path: '/'\n      }, {\n        name: 'second',\n        method: 'GET',\n        path: '/'\n      }]\n\n      function request () {\n        const options = queue.shift()\n        if (options.ciphers) {\n          // Choose different cipher to use different cache entry\n          tls.ciphers = options.ciphers\n        } else {\n          delete tls.ciphers\n        }\n        client.request(options, (err, data) => {\n          t.ifError(err)\n          clientSessions[options.name] = client[kSocket].getSession()\n          data.body.resume().on('end', () => {\n            if (queue.length !== 0) {\n              return request()\n            }\n            t.ok(true, 'pass')\n          })\n        })\n      }\n      request()\n    })\n\n    await t.completed\n  })\n\n  test('Verify cached sessions', async t => {\n    t = tspl(t, { plan: 2 })\n    t.strictEqual(serverRequests, 2)\n    t.notEqual(\n      clientSessions.first.toString('hex'),\n      clientSessions.second.toString('hex')\n    )\n    await t.completed\n  })\n})\n\ndescribe('A pool should be able to reuse TLS sessions between clients', () => {\n  let serverRequests = 0\n\n  const REQ_COUNT = 10\n  const ASSERT_PERFORMANCE_GAIN = false\n\n  test('Prepare request', async t => {\n    t = tspl(t, { plan: 2 + 1 + (ASSERT_PERFORMANCE_GAIN ? 1 : 0) })\n    const server = https.createServer(options, (req, res) => {\n      serverRequests++\n      res.end()\n    })\n\n    let numSessions = 0\n    const sessions = []\n\n    server.listen(0, async () => {\n      const poolWithSessionReuse = new Pool(`https://localhost:${server.address().port}`, {\n        pipelining: 0,\n        connections: 100,\n        maxCachedSessions: 1,\n        tls: {\n          ca,\n          rejectUnauthorized: false,\n          servername: 'agent1'\n        }\n      })\n      const poolWithoutSessionReuse = new Pool(`https://localhost:${server.address().port}`, {\n        pipelining: 0,\n        connections: 100,\n        maxCachedSessions: 0,\n        tls: {\n          ca,\n          rejectUnauthorized: false,\n          servername: 'agent1'\n        }\n      })\n\n      poolWithSessionReuse.on('connect', (url, targets) => {\n        const y = targets[1][kSocket].getSession()\n        if (sessions.some(x => x.equals(y))) {\n          return\n        }\n        sessions.push(y)\n        numSessions++\n      })\n\n      after(() => {\n        poolWithSessionReuse.close()\n        poolWithoutSessionReuse.close()\n        server.close()\n      })\n\n      function request (pool, expectTLSSessionCache) {\n        return new Promise((resolve, reject) => {\n          pool.request({\n            method: 'GET',\n            path: '/'\n          }, (err, data) => {\n            if (err) return reject(err)\n            data.body.resume().on('end', resolve)\n          })\n        })\n      }\n\n      async function runRequests (pool, numIterations, expectTLSSessionCache) {\n        const requests = []\n        // For the session reuse, we first need one client to connect to receive a valid tls session to reuse\n        await request(pool, false)\n        while (numIterations--) {\n          requests.push(request(pool, expectTLSSessionCache))\n        }\n        return await Promise.all(requests)\n      }\n\n      await runRequests(poolWithoutSessionReuse, REQ_COUNT, false)\n      await runRequests(poolWithSessionReuse, REQ_COUNT, true)\n\n      t.strictEqual(numSessions, 2)\n      t.strictEqual(serverRequests, 2 + REQ_COUNT * 2)\n      t.ok(true, 'pass')\n    })\n\n    await t.completed\n  })\n})\n"
  },
  {
    "path": "test/tls.js",
    "content": "'use strict'\n\n// TODO: Don't depend on external URLs.\n\n// const { test } = require('tap')\n// const { Client } = require('..')\n// const { kSocket } = require('../lib/core/symbols')\n// const { Readable } = require('node:stream')\n// const { kRunning } = require('../lib/core/symbols')\n\n// test('tls get 1', (t) => {\n//   t.plan(4)\n\n//   const client = new Client('https://www.github.com')\n//   t.teardown(client.close.bind(client))\n\n//   client.request({ method: 'GET', path: '/' }, (err, data) => {\n//     t.error(err)\n//     t.equal(data.statusCode, 301)\n//     t.equal(client[kSocket].authorized, true)\n\n//     data.body\n//       .resume()\n//       .on('end', () => {\n//         t.ok(true, 'pass')\n//       })\n//   })\n// })\n\n// test('tls get 2', (t) => {\n//   t.plan(4)\n\n//   const client = new Client('https://140.82.112.4', {\n//     tls: {\n//       servername: 'www.github.com'\n//     }\n//   })\n//   t.teardown(client.close.bind(client))\n\n//   client.request({ method: 'GET', path: '/' }, (err, data) => {\n//     t.error(err)\n//     t.equal(data.statusCode, 301)\n//     t.equal(client[kSocket].authorized, true)\n\n//     data.body\n//       .resume()\n//       .on('end', () => {\n//         t.ok(true, 'pass')\n//       })\n//   })\n// })\n\n// test('tls get 3', (t) => {\n//   t.plan(8)\n\n//   const client = new Client('https://140.82.112.4')\n//   t.teardown(client.destroy.bind(client))\n\n//   let didDisconnect = false\n//   client.request({\n//     method: 'GET',\n//     path: '/',\n//     headers: {\n//       host: 'www.github.com'\n//     }\n//   }, (err, data) => {\n//     t.error(err)\n//     t.equal(data.statusCode, 301)\n//     t.equal(client[kSocket].authorized, true)\n\n//     data.body\n//       .resume()\n//       .on('end', () => {\n//         t.ok(true, 'pass')\n//       })\n//     client.once('disconnect', () => {\n//       t.ok(true, 'pass')\n//       didDisconnect = true\n//     })\n//   })\n\n//   const body = new Readable({ read () {} })\n//   body.on('error', (err) => {\n//     t.ok(err)\n//   })\n//   client.request({\n//     method: 'POST',\n//     path: '/',\n//     body,\n//     headers: {\n//       host: 'www.asd.com'\n//     }\n//   }, (err, data) => {\n//     t.equal(didDisconnect, true)\n//     t.ok(err)\n//   })\n// })\n\n// test('tls get 4', (t) => {\n//   t.plan(9)\n\n//   const client = new Client('https://140.82.112.4', {\n//     tls: {\n//       servername: 'www.github.com'\n//     },\n//     pipelining: 2\n//   })\n//   t.teardown(client.close.bind(client))\n\n//   client.request({\n//     method: 'GET',\n//     path: '/',\n//     headers: {\n//       host: '140.82.112.4'\n//     }\n//   }, (err, data) => {\n//     t.error(err)\n//     t.equal(client[kRunning], 1)\n//     t.equal(data.statusCode, 301)\n//     t.equal(client[kSocket].authorized, true)\n\n//     client.request({\n//       method: 'GET',\n//       path: '/',\n//       headers: {\n//         host: 'www.github.com'\n//       }\n//     }, (err, data) => {\n//       t.error(err)\n//       t.equal(data.statusCode, 301)\n//       t.equal(client[kSocket].authorized, true)\n\n//       data.body\n//         .resume()\n//         .on('end', () => {\n//           t.ok(true, 'pass')\n//         })\n//     })\n\n//     data.body\n//       .resume()\n//       .on('end', () => {\n//         t.ok(true, 'pass')\n//       })\n//   })\n// })\n\n// test('tls get 5', (t) => {\n//   t.plan(7)\n\n//   const client = new Client('https://140.82.112.4')\n//   t.teardown(client.destroy.bind(client))\n\n//   let didDisconnect = false\n//   client.request({\n//     method: 'GET',\n//     path: '/',\n//     headers: {\n//       host: 'www.github.com'\n//     }\n//   }, (err, data) => {\n//     t.error(err)\n//     t.equal(data.statusCode, 301)\n//     t.equal(client[kSocket].authorized, true)\n\n//     data.body\n//       .resume()\n//       .on('end', () => {\n//         t.ok(true, 'pass')\n//       })\n//     client.once('disconnect', () => {\n//       t.ok(true, 'pass')\n//       didDisconnect = true\n//     })\n//   })\n\n//   client.request({\n//     method: 'POST',\n//     path: '/',\n//     body: [],\n//     headers: {\n//       host: 'www.asd.com'\n//     }\n//   }, (err, data) => {\n//     t.equal(didDisconnect, true)\n//     t.ok(err)\n//   })\n// })\n"
  },
  {
    "path": "test/trailers.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client } = require('..')\nconst { createServer } = require('node:http')\n\ntest('response trailers missing is OK', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      Trailer: 'content-length'\n    })\n    res.end('response')\n  })\n  after(() => server.close())\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const { body } = await client.request({\n      path: '/',\n      method: 'GET',\n      body: 'asd'\n    })\n\n    t.strictEqual(await body.text(), 'response')\n  })\n\n  await t.completed\n})\n\ntest('response trailers missing w trailers is OK', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    res.writeHead(200, {\n      Trailer: 'content-length'\n    })\n    res.addTrailers({\n      asd: 'foo'\n    })\n    res.end('response')\n  })\n  after(() => server.close())\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.destroy())\n\n    client.on('disconnect', () => {\n      if (!client.closed && !client.destroyed) {\n        t.fail('unexpected disconnect')\n      }\n    })\n\n    const { body, trailers } = await client.request({\n      path: '/',\n      method: 'GET',\n      body: 'asd'\n    })\n\n    t.strictEqual(await body.text(), 'response')\n    t.deepStrictEqual(trailers, { asd: 'foo' })\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/types/agent.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable } from 'tsd'\nimport { Agent, Dispatcher } from '../..'\nimport { URL } from 'node:url'\n\nexpectAssignable<Agent>(new Agent())\nexpectAssignable<Agent>(new Agent({}))\nexpectAssignable<Agent>(new Agent({ factory: () => new Dispatcher() }))\n\n{\n  const agent = new Agent()\n\n  // properties\n  expectAssignable<boolean>(agent.closed)\n  expectAssignable<boolean>(agent.destroyed)\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: '', path: '', method: 'GET' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }))\n  expectAssignable<void>(agent.request({ origin: '', path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<void>(agent.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<void>(agent.stream(\n    { origin: '', path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(agent.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n\n  // pipeline\n  expectAssignable<Duplex>(agent.pipeline({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(agent.pipeline({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(agent.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(agent.upgrade({ path: '' }))\n  expectAssignable<void>(agent.upgrade({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.UpgradeData>(data)\n  }))\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(agent.connect({ origin: '', path: '' }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(agent.connect({ origin: new URL('http://localhost'), path: '' }))\n  expectAssignable<void>(agent.connect({ origin: '', path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n  expectAssignable<void>(agent.connect({ origin: new URL('http://localhost'), path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(agent.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(agent.close())\n  expectAssignable<void>(agent.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(agent.destroy())\n  expectAssignable<Promise<void>>(agent.destroy(new Error()))\n  expectAssignable<Promise<void>>(agent.destroy(null))\n  expectAssignable<void>(agent.destroy(() => {}))\n  expectAssignable<void>(agent.destroy(new Error(), () => {}))\n  expectAssignable<void>(agent.destroy(null, () => {}))\n}\n"
  },
  {
    "path": "test/types/api.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable, expectType } from 'tsd'\nimport { Dispatcher, request, stream, pipeline, connect, upgrade } from '../..'\n\n// request\nexpectAssignable<Promise<Dispatcher.ResponseData>>(request(''))\nexpectAssignable<Promise<Dispatcher.ResponseData>>(request('', { }))\nexpectAssignable<Promise<Dispatcher.ResponseData>>(request('', { method: 'GET', reset: false }))\n\n// stream\nexpectAssignable<Promise<Dispatcher.StreamData>>(stream('', { method: 'GET' }, data => {\n  expectAssignable<Dispatcher.StreamFactoryData>(data)\n  expectType<null>(data.opaque)\n  return new Writable()\n}))\nexpectAssignable<Promise<Dispatcher.StreamData<{ example: string }>>>(stream('', { method: 'GET', opaque: { example: '' } }, data => {\n  expectType<{ example: string }>(data.opaque)\n  return new Writable()\n}))\n\n// pipeline\nexpectAssignable<Duplex>(pipeline('', { method: 'GET' }, data => {\n  expectAssignable<Dispatcher.PipelineHandlerData>(data)\n  expectType<null>(data.opaque)\n  return new Readable()\n}))\nexpectAssignable<Duplex>(pipeline('', { method: 'GET', opaque: { example: '' } }, data => {\n  expectType<{ example: string }>(data.opaque)\n  return new Readable()\n}))\n\n// connect\nexpectAssignable<Promise<Dispatcher.ConnectData>>(connect(''))\nexpectAssignable<Promise<Dispatcher.ConnectData>>(connect('', {}))\n\n// upgrade\nexpectAssignable<Promise<Dispatcher.UpgradeData>>(upgrade(''))\nexpectAssignable<Promise<Dispatcher.UpgradeData>>(upgrade('', {}))\n"
  },
  {
    "path": "test/types/balanced-pool.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable } from 'tsd'\nimport { Dispatcher, BalancedPool, Client, Pool } from '../..'\nimport { URL } from 'node:url'\n\nexpectAssignable<BalancedPool>(new BalancedPool(''))\nexpectAssignable<BalancedPool>(new BalancedPool('', {}))\nexpectAssignable<BalancedPool>(new BalancedPool(new URL('http://localhost'), {}))\nexpectAssignable<BalancedPool>(new BalancedPool('', { factory: () => new Dispatcher() }))\nexpectAssignable<BalancedPool>(new BalancedPool('', { factory: (origin, opts) => new Client(origin, opts) }))\nexpectAssignable<BalancedPool>(new BalancedPool('', { connections: 1 }))\nexpectAssignable<BalancedPool>(new BalancedPool(['http://localhost:4242', 'http://www.nodejs.org']))\nexpectAssignable<BalancedPool>(new BalancedPool([new URL('http://localhost:4242'), new URL('http://www.nodejs.org')], {}))\n\n{\n  const pool = new BalancedPool('', {})\n\n  // properties\n  expectAssignable<boolean>(pool.closed)\n  expectAssignable<boolean>(pool.destroyed)\n\n  // upstreams\n  expectAssignable<BalancedPool>(pool.addUpstream('http://www.nodejs.org'))\n  expectAssignable<BalancedPool>(pool.removeUpstream('http://www.nodejs.org'))\n  expectAssignable<BalancedPool>(pool.addUpstream(new URL('http://www.nodejs.org')))\n  expectAssignable<BalancedPool>(pool.removeUpstream(new URL('http://www.nodejs.org')))\n  expectAssignable<Pool | undefined>(pool.getUpstream('http://www.nodejs.org'))\n  expectAssignable<Pool | undefined>(pool.getUpstream(new URL('http://www.nodejs.org')))\n  expectAssignable<string[]>(pool.upstreams)\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(pool.request({ origin: '', path: '', method: 'GET' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(pool.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }))\n  expectAssignable<void>(pool.request({ origin: '', path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<void>(pool.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(pool.stream({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(pool.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<void>(pool.stream(\n    { origin: '', path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(pool.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n\n  // pipeline\n  expectAssignable<Duplex>(pool.pipeline({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(pool.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(pool.upgrade({ path: '' }))\n  expectAssignable<void>(pool.upgrade({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.UpgradeData>(data)\n  }))\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(pool.connect({ path: '' }))\n  expectAssignable<void>(pool.connect({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(pool.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n  expectAssignable<boolean>(pool.dispatch({ origin: new URL('http://localhost'), path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(pool.close())\n  expectAssignable<void>(pool.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(pool.destroy())\n  expectAssignable<Promise<void>>(pool.destroy(new Error()))\n  expectAssignable<Promise<void>>(pool.destroy(null))\n  expectAssignable<void>(pool.destroy(() => {}))\n  expectAssignable<void>(pool.destroy(new Error(), () => {}))\n  expectAssignable<void>(pool.destroy(null, () => {}))\n}\n"
  },
  {
    "path": "test/types/cache-interceptor.test-d.ts",
    "content": "import { Writable } from 'node:stream'\nimport { expectAssignable, expectNotAssignable } from 'tsd'\nimport CacheInterceptor from '../../types/cache-interceptor'\n\nconst store: CacheInterceptor.CacheStore = {\n  get (_: CacheInterceptor.CacheKey): CacheInterceptor.GetResult | Promise<CacheInterceptor.GetResult | undefined> | undefined {\n    throw new Error('stub')\n  },\n\n  createWriteStream (_: CacheInterceptor.CacheKey, _2: CacheInterceptor.CacheValue): Writable | undefined {\n    throw new Error('stub')\n  },\n\n  delete (_: CacheInterceptor.CacheKey): void | Promise<void> {\n    throw new Error('stub')\n  }\n}\n\nexpectAssignable<CacheInterceptor.CacheOptions>({})\nexpectAssignable<CacheInterceptor.CacheOptions>({ store })\nexpectAssignable<CacheInterceptor.CacheOptions>({ methods: [] })\nexpectAssignable<CacheInterceptor.CacheOptions>({ store, methods: ['GET'] })\n\n// origins option type tests\nexpectAssignable<CacheInterceptor.CacheOptions>({ origins: undefined })\nexpectAssignable<CacheInterceptor.CacheOptions>({ origins: [] })\nexpectAssignable<CacheInterceptor.CacheOptions>({ origins: ['http://localhost'] })\nexpectAssignable<CacheInterceptor.CacheOptions>({ origins: [/localhost/] })\nexpectAssignable<CacheInterceptor.CacheOptions>({ origins: ['http://example.com', /localhost/] })\nexpectNotAssignable<CacheInterceptor.CacheOptions>({ origins: 'http://localhost' })\nexpectNotAssignable<CacheInterceptor.CacheOptions>({ origins: [123] })\nexpectNotAssignable<CacheInterceptor.CacheOptions>({ origins: [null] })\n\nexpectAssignable<CacheInterceptor.CacheValue>({\n  statusCode: 200,\n  statusMessage: 'OK',\n  headers: {},\n  cacheControlDirectives: {},\n  cachedAt: 0,\n  staleAt: 0,\n  deleteAt: 0\n})\n\nexpectAssignable<CacheInterceptor.CacheValue>({\n  statusCode: 200,\n  statusMessage: 'OK',\n  headers: {},\n  vary: {\n    foo: 'bar'\n  },\n  cacheControlDirectives: {\n    'max-stale': 0,\n    'min-fresh': 0,\n    'max-age': 0,\n    's-maxage': 0,\n    'stale-while-revalidate': 0,\n    'stale-if-error': 0,\n    public: true,\n    private: true,\n    'no-store': true,\n    'no-cache': true,\n    'must-revalidate': true,\n    'proxy-revalidate': true,\n    immutable: true,\n    'no-transform': true,\n    'must-understand': true,\n    'only-if-cached': true\n  },\n  cachedAt: 0,\n  staleAt: 0,\n  deleteAt: 0\n})\n\nexpectAssignable<CacheInterceptor.CacheControlDirectives>({\n  private: [''],\n  'no-cache': ['']\n})\n\nexpectNotAssignable<CacheInterceptor.CacheValue>({})\nexpectNotAssignable<CacheInterceptor.CacheValue>({\n  statusCode: '123',\n  statusMessage: 123,\n  rawHeaders: '',\n  vary: '',\n  size: '',\n  cachedAt: '',\n  staleAt: '',\n  deleteAt: ''\n})\n\nexpectAssignable<CacheInterceptor.CacheValue>({\n  statusCode: 200,\n  statusMessage: 'OK',\n  headers: {},\n  vary: {\n    'accept-encoding': null,\n    authorization: 'example-value'\n  },\n  cachedAt: 0,\n  staleAt: 0,\n  deleteAt: 0\n})\n\nexpectAssignable<CacheInterceptor.CacheValue>({\n  statusCode: 200,\n  statusMessage: 'OK',\n  headers: {},\n  vary: {\n    'accept-encoding': null,\n    authorization: null\n  },\n  cachedAt: 0,\n  staleAt: 0,\n  deleteAt: 0\n})\n\nexpectNotAssignable<CacheInterceptor.CacheValue>({\n  statusCode: 200,\n  statusMessage: 'OK',\n  headers: {},\n  vary: {\n    'accept-encoding': undefined,\n    authorization: 'example-value'\n  },\n  cachedAt: 0,\n  staleAt: 0,\n  deleteAt: 0\n})\n\nexpectAssignable<CacheInterceptor.MemoryCacheStoreOpts>({})\nexpectAssignable<CacheInterceptor.MemoryCacheStoreOpts>({\n  maxSize: 0\n})\n\nexpectAssignable<CacheInterceptor.SqliteCacheStoreOpts>({})\nexpectAssignable<CacheInterceptor.SqliteCacheStoreOpts>({\n  location: '',\n  maxEntrySize: 0\n})\n"
  },
  {
    "path": "test/types/cache-storage.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport {\n  caches,\n  CacheStorage,\n  Cache,\n  CacheQueryOptions,\n  MultiCacheQueryOptions,\n  RequestInfo,\n  Request,\n  Response\n} from '../..'\n\ndeclare const response: Response\ndeclare const request: Request\ndeclare const options: RequestInfo\ndeclare const cache: Cache\n\nexpectAssignable<CacheStorage>(caches)\nexpectAssignable<MultiCacheQueryOptions>({})\nexpectAssignable<MultiCacheQueryOptions>({ cacheName: 'v1' })\nexpectAssignable<MultiCacheQueryOptions>({ ignoreMethod: false, ignoreSearch: true })\n\nexpectAssignable<CacheQueryOptions>({})\nexpectAssignable<CacheQueryOptions>({ ignoreVary: false, ignoreMethod: true, ignoreSearch: true })\n\nexpectAssignable<Promise<Cache>>(caches.open('v1'))\nexpectAssignable<Promise<Response | undefined>>(caches.match(options))\nexpectAssignable<Promise<Response | undefined>>(caches.match(request))\nexpectAssignable<Promise<boolean>>(caches.has('v1'))\nexpectAssignable<Promise<boolean>>(caches.delete('v1'))\nexpectAssignable<Promise<string[]>>(caches.keys())\n\nexpectAssignable<Promise<Response | undefined>>(cache.match(options))\nexpectAssignable<Promise<readonly Response[]>>(cache.matchAll('v1'))\nexpectAssignable<Promise<boolean>>(cache.delete('v1'))\nexpectAssignable<Promise<readonly Request[]>>(cache.keys())\nexpectAssignable<Promise<undefined>>(cache.add(options))\nexpectAssignable<Promise<undefined>>(cache.addAll([options]))\nexpectAssignable<Promise<undefined>>(cache.put(options, response))\n"
  },
  {
    "path": "test/types/client.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable, expectType } from 'tsd'\nimport { Client, Dispatcher } from '../..'\nimport { URL } from 'node:url'\n\nexpectAssignable<Client>(new Client(''))\nexpectAssignable<Client>(new Client('', {}))\nexpectAssignable<Client>(\n  new Client('', {\n    maxRequestsPerClient: 10\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    connect: { rejectUnauthorized: false }\n  })\n)\nexpectAssignable<Client>(new Client(new URL('http://localhost'), {}))\n\n/**\n * Tests for Client.Options:\n */\nexpectAssignable<Client>(\n  new Client('', {\n    maxHeaderSize: 16384\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    headersTimeout: 300e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    connectTimeout: 300e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    bodyTimeout: 300e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    keepAliveTimeout: 4e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    keepAliveMaxTimeout: 600e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    keepAliveTimeoutThreshold: 1e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    socketPath: '/var/run/docker.sock'\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    pipelining: 1\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    strictContentLength: true\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    maxCachedSessions: 1\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    maxCachedSessions: 1\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    maxRequestsPerClient: 1\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    localAddress: '127.0.0.1'\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    maxResponseSize: -1\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    autoSelectFamily: true\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    autoSelectFamilyAttemptTimeout: 300e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    allowH2: true\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    maxConcurrentStreams: 100\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    initialWindowSize: 262144\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    connectionWindowSize: 524288\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    pingInterval: 60e3\n  })\n)\nexpectAssignable<Client>(\n  new Client('', {\n    interceptors: {\n      Client: [\n        (dispatcher) => {\n          expectAssignable<Dispatcher['dispatch']>(dispatcher)\n          return (opts, handlers) => {\n            expectAssignable<Dispatcher.DispatchOptions>(opts)\n            expectAssignable<Dispatcher.DispatchHandler>(handlers)\n            return dispatcher(opts, handlers)\n          }\n        }\n      ]\n    }\n  })\n)\n\n{\n  const client = new Client('')\n\n  // properties\n  expectAssignable<number>(client.pipelining)\n  expectAssignable<boolean>(client.closed)\n  expectAssignable<boolean>(client.destroyed)\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(\n    client.request({ origin: '', path: '', method: 'GET' })\n  )\n  expectAssignable<Promise<Dispatcher.ResponseData>>(\n    client.request({\n      origin: new URL('http://localhost:3000'),\n      path: '',\n      method: 'GET'\n    })\n  )\n  expectAssignable<void>(\n    client.request({ origin: '', path: '', method: 'GET' }, (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.ResponseData>(data)\n    })\n  )\n  expectAssignable<void>(\n    client.request(\n      { origin: new URL('http://localhost:3000'), path: '', method: 'GET' },\n      (err, data) => {\n        expectAssignable<Error | null>(err)\n        expectAssignable<Dispatcher.ResponseData>(data)\n      }\n    )\n  )\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(\n    client.stream({ origin: '', path: '', method: 'GET' }, (data) => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    })\n  )\n  expectAssignable<Promise<Dispatcher.StreamData>>(\n    client.stream(\n      { origin: new URL('http://localhost'), path: '', method: 'GET' },\n      (data) => {\n        expectAssignable<Dispatcher.StreamFactoryData>(data)\n        return new Writable()\n      }\n    )\n  )\n  expectAssignable<void>(\n    client.stream(\n      { origin: '', path: '', method: 'GET' },\n      (data) => {\n        expectAssignable<Dispatcher.StreamFactoryData>(data)\n        return new Writable()\n      },\n      (err, data) => {\n        expectAssignable<Error | null>(err)\n        expectAssignable<Dispatcher.StreamData>(data)\n      }\n    )\n  )\n  expectAssignable<void>(\n    client.stream(\n      { origin: new URL('http://localhost'), path: '', method: 'GET' },\n      (data) => {\n        expectAssignable<Dispatcher.StreamFactoryData>(data)\n        return new Writable()\n      },\n      (err, data) => {\n        expectAssignable<Error | null>(err)\n        expectAssignable<Dispatcher.StreamData>(data)\n      }\n    )\n  )\n\n  // pipeline\n  expectAssignable<Duplex>(\n    client.pipeline({ origin: '', path: '', method: 'GET' }, (data) => {\n      expectAssignable<Dispatcher.PipelineHandlerData>(data)\n      return new Readable()\n    })\n  )\n  expectAssignable<Duplex>(\n    client.pipeline(\n      { origin: new URL('http://localhost'), path: '', method: 'GET' },\n      (data) => {\n        expectAssignable<Dispatcher.PipelineHandlerData>(data)\n        return new Readable()\n      }\n    )\n  )\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(\n    client.upgrade({ path: '' })\n  )\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(\n    client.upgrade({ path: '', headers: [] })\n  )\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(\n    client.upgrade({ path: '', headers: {} })\n  )\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(\n    client.upgrade({ path: '', headers: null })\n  )\n  expectAssignable<void>(\n    client.upgrade({ path: '' }, (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.UpgradeData>(data)\n    })\n  )\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(\n    client.connect({ path: '' })\n  )\n  expectAssignable<Promise<Dispatcher.ConnectData>>(\n    client.connect({ path: '', headers: [] })\n  )\n  expectAssignable<Promise<Dispatcher.ConnectData>>(\n    client.connect({ path: '', headers: {} })\n  )\n  expectAssignable<Promise<Dispatcher.ConnectData>>(\n    client.connect({ path: '', headers: null })\n  )\n  expectAssignable<void>(\n    client.connect({ path: '' }, (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.ConnectData>(data)\n    })\n  )\n\n  // dispatch\n  expectAssignable<boolean>(\n    client.dispatch({ origin: '', path: '', method: 'GET' }, {})\n  )\n  expectAssignable<boolean>(\n    client.dispatch({ origin: '', path: '', method: 'GET', headers: [] }, {})\n  )\n  expectAssignable<boolean>(\n    client.dispatch({ origin: '', path: '', method: 'GET', headers: {} }, {})\n  )\n  expectAssignable<boolean>(\n    client.dispatch({ origin: '', path: '', method: 'GET', headers: null }, {})\n  )\n  expectAssignable<boolean>(\n    client.dispatch(\n      { origin: new URL('http://localhost'), path: '', method: 'GET' },\n      {}\n    )\n  )\n\n  // close\n  expectAssignable<Promise<void>>(client.close())\n  expectAssignable<void>(client.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(client.destroy())\n  expectAssignable<Promise<void>>(client.destroy(new Error()))\n  expectAssignable<Promise<void>>(client.destroy(null))\n  expectAssignable<void>(client.destroy(() => {}))\n  expectAssignable<void>(client.destroy(new Error(), () => {}))\n  expectAssignable<void>(client.destroy(null, () => {}))\n\n  // stats\n  expectType<boolean>(client.stats.connected)\n  expectType<number>(client.stats.pending)\n  expectType<number>(client.stats.running)\n  expectType<number>(client.stats.size)\n}\n"
  },
  {
    "path": "test/types/connector.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { Client, buildConnector } from '../..'\nimport { TLSSocket } from 'node:tls'\nimport { Socket } from 'node:net'\n\nconst connector = buildConnector({ rejectUnauthorized: false, allowH2: false })\nexpectAssignable<Client>(new Client('', {\n  connect (opts: buildConnector.Options, cb: buildConnector.Callback) {\n    connector(opts, (...args) => {\n      if (args[0]) {\n        return cb(args[0], null)\n      }\n      if (args[1] instanceof TLSSocket) {\n        if (args[1].getPeerCertificate().fingerprint256 !== 'FO:OB:AR') {\n          args[1].destroy()\n          return cb(new Error('Fingerprint does not match'), null)\n        }\n      }\n      return cb(null, args[1])\n    })\n  }\n}))\n\nexpectAssignable<buildConnector.BuildOptions>({\n  checkServerIdentity: () => undefined, // Test if ConnectionOptions is assignable\n  localPort: 1234, // Test if TcpNetConnectOpts is assignable\n  keepAlive: true,\n  keepAliveInitialDelay: 12345\n})\n\nexpectAssignable<buildConnector.Options>({\n  protocol: 'http',\n  hostname: 'example.com',\n  port: '',\n  localAddress: '127.0.0.1',\n  socketPath: '/var/run/undici.sock',\n  httpSocket: new Socket()\n})\n"
  },
  {
    "path": "test/types/diagnostics-channel.test-d.ts",
    "content": "import { Socket } from 'node:net'\nimport { expectAssignable } from 'tsd'\nimport { DiagnosticsChannel, buildConnector } from '../..'\n\nconst request = {\n  origin: '',\n  completed: true,\n  method: 'GET' as const,\n  path: '',\n  headers: '',\n  addHeader: (key: string, value: string) => {\n    return request\n  }\n}\n\nconst response = {\n  statusCode: 200,\n  statusText: 'OK',\n  headers: [Buffer.from(''), Buffer.from('')]\n}\n\nconst connectParams = {\n  host: '',\n  hostname: '',\n  protocol: '',\n  port: '',\n  servername: ''\n}\n\nexpectAssignable<DiagnosticsChannel.RequestCreateMessage>({ request })\nexpectAssignable<DiagnosticsChannel.RequestBodyChunkSentMessage>({ request, chunk: Buffer.from('') })\nexpectAssignable<DiagnosticsChannel.RequestBodyChunkSentMessage>({ request, chunk: '' })\nexpectAssignable<DiagnosticsChannel.RequestBodySentMessage>({ request })\nexpectAssignable<DiagnosticsChannel.RequestHeadersMessage>({\n  request,\n  response\n})\nexpectAssignable<DiagnosticsChannel.RequestBodyChunkReceivedMessage>({ request, chunk: Buffer.from('') })\nexpectAssignable<DiagnosticsChannel.RequestTrailersMessage>({\n  request,\n  trailers: [Buffer.from(''), Buffer.from('')]\n})\nexpectAssignable<DiagnosticsChannel.RequestErrorMessage>({\n  request,\n  error: new Error('Error')\n})\nexpectAssignable<DiagnosticsChannel.ClientSendHeadersMessage>({\n  request,\n  headers: '',\n  socket: new Socket()\n})\nexpectAssignable<DiagnosticsChannel.ClientBeforeConnectMessage>({\n  connectParams,\n  connector: (\n    options: buildConnector.Options,\n    callback: buildConnector.Callback\n  ) => new Socket()\n})\nexpectAssignable<DiagnosticsChannel.ClientConnectedMessage>({\n  socket: new Socket(),\n  connectParams,\n  connector: (\n    options: buildConnector.Options,\n    callback: buildConnector.Callback\n  ) => new Socket()\n})\nexpectAssignable<DiagnosticsChannel.ClientConnectErrorMessage>({\n  error: new Error('Error'),\n  socket: new Socket(),\n  connectParams,\n  connector: (\n    options: buildConnector.Options,\n    callback: buildConnector.Callback\n  ) => new Socket()\n})\n"
  },
  {
    "path": "test/types/dispatcher.events.test-d.ts",
    "content": "import { Dispatcher } from '../..'\nimport { expectAssignable } from 'tsd'\nimport { URL } from 'node:url'\nimport Errors from '../../types/errors'\n\ninterface EventHandler {\n  connect(origin: URL, targets: readonly Dispatcher[]): void\n  disconnect(origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): void\n  connectionError(origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): void\n  drain(origin: URL): void\n}\n\n{\n  const dispatcher = new Dispatcher()\n  const eventHandler: EventHandler = {} as EventHandler\n\n  expectAssignable<EventHandler['connect'][]>(dispatcher.rawListeners('connect'))\n  expectAssignable<EventHandler['disconnect'][]>(dispatcher.rawListeners('disconnect'))\n  expectAssignable<EventHandler['connectionError'][]>(dispatcher.rawListeners('connectionError'))\n  expectAssignable<EventHandler['drain'][]>(dispatcher.rawListeners('drain'))\n\n  expectAssignable<EventHandler['connect'][]>(dispatcher.listeners('connect'))\n  expectAssignable<EventHandler['disconnect'][]>(dispatcher.listeners('disconnect'))\n  expectAssignable<EventHandler['connectionError'][]>(dispatcher.listeners('connectionError'))\n  expectAssignable<EventHandler['drain'][]>(dispatcher.listeners('drain'))\n\n  const eventHandlerMethods: ['on', 'once', 'off', 'addListener', 'removeListener', 'prependListener', 'prependOnceListener'] =\n    ['on', 'once', 'off', 'addListener', 'removeListener', 'prependListener', 'prependOnceListener']\n\n  for (const method of eventHandlerMethods) {\n    expectAssignable<Dispatcher>(dispatcher[method]('connect', eventHandler['connect']))\n    expectAssignable<Dispatcher>(dispatcher[method]('disconnect', eventHandler['disconnect']))\n    expectAssignable<Dispatcher>(dispatcher[method]('connectionError', eventHandler['connectionError']))\n    expectAssignable<Dispatcher>(dispatcher[method]('drain', eventHandler['drain']))\n  }\n\n  const origin = new URL('')\n  const targets = new Array<Dispatcher>()\n  const error = new Errors.UndiciError()\n  expectAssignable<boolean>(dispatcher.emit('connect', origin, targets))\n  expectAssignable<boolean>(dispatcher.emit('disconnect', origin, targets, error))\n  expectAssignable<boolean>(dispatcher.emit('connectionError', origin, targets, error))\n  expectAssignable<boolean>(dispatcher.emit('drain', origin))\n}\n"
  },
  {
    "path": "test/types/dispatcher.test-d.ts",
    "content": "import { IncomingHttpHeaders } from 'node:http'\nimport { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable, expectType } from 'tsd'\nimport { Dispatcher, Headers } from '../..'\nimport { URL } from 'node:url'\nimport { Blob } from 'node:buffer'\n\nexpectAssignable<Dispatcher>(new Dispatcher())\n\n{\n  const dispatcher = new Dispatcher()\n\n  const nodeCoreHeaders = {\n    authorization: undefined,\n    'content-type': 'application/json'\n  } satisfies IncomingHttpHeaders\n\n  const headerInstanceHeaders = new Headers({ hello: 'world' })\n  const mapHeaders = new Map([['hello', 'world']])\n  const iteratorHeaders = {\n    * [Symbol.iterator] () {\n      yield ['hello', 'world']\n    }\n  }\n  const recordHeadersString: Record<string, string> = { hello: 'world' }\n  const recordHeadersStringArray: Record<string, string[]> = { foo: ['hello', 'world'] }\n  const recordHeadersUndefined: Record<string, undefined> = { bar: undefined }\n\n  // dispatch\n  expectAssignable<boolean>(dispatcher.dispatch({ path: '', method: 'GET' }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: { authorization: undefined } }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: [] }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: ['hello', 'world'] }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: {} }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: nodeCoreHeaders }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: null, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: undefined, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: recordHeadersString, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: recordHeadersStringArray, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: recordHeadersUndefined, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: headerInstanceHeaders, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: mapHeaders, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: '', path: '', method: 'GET', headers: iteratorHeaders, reset: true }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ origin: new URL('http://localhost'), path: '', method: 'GET' }, {}))\n  expectAssignable<boolean>(dispatcher.dispatch({ path: '', method: 'CUSTOM' }, {}))\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '' }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: new URL('http://localhost'), path: '' }))\n  expectAssignable<void>(dispatcher.connect({ origin: '', path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n  expectAssignable<void>(dispatcher.connect({ origin: new URL('http://localhost'), path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', responseHeaders: 'raw' }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', responseHeaders: null }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: { authorization: undefined } }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: [] }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: ['hello', 'world'] }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: {} }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: nodeCoreHeaders }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: null }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: undefined }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: recordHeadersString }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: recordHeadersStringArray }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: recordHeadersUndefined }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: headerInstanceHeaders }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: mapHeaders }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(dispatcher.connect({ origin: '', path: '', headers: iteratorHeaders }))\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', query: {} }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', query: { pageNum: 1, id: 'abc' } }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', throwOnError: true }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }))\n  expectAssignable<void>(dispatcher.request({ origin: '', path: '', method: 'GET', reset: true }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<void>(dispatcher.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', responseHeaders: 'raw' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', responseHeaders: null }))\n  expectAssignable<Promise<Dispatcher.ResponseData<{ example: string }>>>(dispatcher.request({ origin: '', path: '', method: 'GET', opaque: { example: '' } }))\n\n  // pipeline\n  expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(dispatcher.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET', responseHeaders: 'raw' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET', responseHeaders: null }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET', opaque: { example: '' } }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData<{ example: string }>>(data)\n    expectType<{ example: string }>(data.opaque)\n    return new Readable()\n  }))\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData<{ example: string }>>>(dispatcher.stream({ origin: '', path: '', method: 'GET', opaque: { example: '' } }, data => {\n    expectType<{ example: string }>(data.opaque)\n    return new Writable()\n  }))\n  expectAssignable<void>(dispatcher.stream(\n    { origin: '', path: '', method: 'GET', reset: false },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(dispatcher.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(dispatcher.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET', opaque: { example: '' } },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData<{ example: string }>>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData<{ example: string }>>(data)\n      expectType<{ example: string }>(data.opaque)\n    }\n  ))\n  expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: '', path: '', method: 'GET', responseHeaders: 'raw' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: '', path: '', method: 'GET', responseHeaders: null }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '' }))\n  expectAssignable<void>(dispatcher.upgrade({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.UpgradeData>(data)\n  }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', responseHeaders: 'raw' }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', responseHeaders: null }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: { authorization: undefined } }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: [] }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: ['hello', 'world'] }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: {} }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: nodeCoreHeaders }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: null }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: undefined }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: recordHeadersString }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: recordHeadersStringArray }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: recordHeadersUndefined }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: headerInstanceHeaders }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: mapHeaders }))\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(dispatcher.upgrade({ path: '', method: 'GET', headers: iteratorHeaders }))\n\n  // close\n  expectAssignable<Promise<void>>(dispatcher.close())\n  expectAssignable<void>(dispatcher.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(dispatcher.destroy())\n  expectAssignable<Promise<void>>(dispatcher.destroy(new Error()))\n  expectAssignable<Promise<void>>(dispatcher.destroy(null))\n  expectAssignable<void>(dispatcher.destroy(() => {}))\n  expectAssignable<void>(dispatcher.destroy(new Error(), () => {}))\n  expectAssignable<void>(dispatcher.destroy(null, () => {}))\n}\n\ndeclare const { body }: Dispatcher.ResponseData\n\n// compose\nexpectAssignable<Dispatcher.ComposedDispatcher>(new Dispatcher().compose(\n  (dispatcher) => {\n    expectAssignable<Dispatcher['dispatch']>(dispatcher)\n    return (opts, handlers) => {\n      expectAssignable<Dispatcher.DispatchOptions>(opts)\n      expectAssignable<Dispatcher.DispatchHandler>(handlers)\n      return dispatcher(opts, handlers)\n    }\n  }\n))\nexpectAssignable<Dispatcher.ComposedDispatcher>(new Dispatcher().compose([\n  (dispatcher) => {\n    expectAssignable<Dispatcher['dispatch']>(dispatcher)\n    return (opts, handlers) => {\n      expectAssignable<Dispatcher.DispatchOptions>(opts)\n      expectAssignable<Dispatcher.DispatchHandler>(handlers)\n      return dispatcher(opts, handlers)\n    }\n  },\n  (dispatcher) => {\n    expectAssignable<Dispatcher['dispatch']>(dispatcher)\n    return (opts, handlers) => {\n      expectAssignable<Dispatcher.DispatchOptions>(opts)\n      expectAssignable<Dispatcher.DispatchHandler>(handlers)\n      return dispatcher(opts, handlers)\n    }\n  }\n]))\n\n// body mixin tests\nexpectType<never | undefined>(body.body)\nexpectType<boolean>(body.bodyUsed)\nexpectType<Promise<ArrayBuffer>>(body.arrayBuffer())\nexpectType<Promise<Blob>>(body.blob())\nexpectType<Promise<never>>(body.formData())\nexpectType<Promise<string>>(body.text())\nexpectType<Promise<unknown>>(body.json())\n"
  },
  {
    "path": "test/types/dns-interceptor.test-d.ts",
    "content": "import { expectAssignable, expectNotAssignable } from 'tsd'\nimport { LookupOptions } from 'node:dns'\nimport Interceptors from '../../types/interceptors'\n\nconst storage: Interceptors.DNSStorage = {\n  get size (): number {\n    throw new Error('stub')\n  },\n  get (origin: string): Interceptors.DNSInterceptorOriginRecords | null {\n    throw new Error('stub')\n  },\n  set (origin: string, records: Interceptors.DNSInterceptorOriginRecords | null, options: { ttl: number }): void {\n    throw new Error('stub')\n  },\n  delete (origin: string): void {\n    throw new Error('stub')\n  },\n  full (): boolean {\n    throw new Error('stub')\n  }\n}\n\nconst lookup: Interceptors.DNSInterceptorOpts['lookup'] = (origin: URL, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: Interceptors.DNSInterceptorRecord[]) => void): void => {\n  throw new Error('stub')\n}\n\nconst pick: Interceptors.DNSInterceptorOpts['pick'] = (origin: URL, records: Interceptors.DNSInterceptorOriginRecords, affinity: 4 | 6): Interceptors.DNSInterceptorRecord => {\n  throw new Error('stub')\n}\n\nexpectAssignable<Interceptors.DNSInterceptorOpts>({})\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ storage })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ maxTTL: 1000 })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ maxItems: 1000 })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ dualStack: true })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ affinity: 4 })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ lookup })\nexpectAssignable<Interceptors.DNSInterceptorOpts>({ pick })\n\nexpectAssignable<Interceptors.DNSInterceptorRecord>({ address: '127.0.0.1', ttl: 300, family: 4 })\n\nexpectAssignable<Interceptors.DNSInterceptorOriginRecords>({ records: { 4: { ips: [{ address: '127.0.0.1', ttl: 300, family: 4 }] }, 6: null } })\n\nexpectNotAssignable<Interceptors.DNSInterceptorOpts>({ storage: new Map() })\nexpectNotAssignable<Interceptors.DNSInterceptorOpts>({\n  lookup: (origin: string) => {\n    throw new Error('stub')\n  }\n})\nexpectNotAssignable<Interceptors.DNSInterceptorOpts>({\n  pick: (origin: string) => {\n    throw new Error('stub')\n  }\n})\n\nexpectNotAssignable<Interceptors.DNSInterceptorRecord>({})\n\nexpectNotAssignable<Interceptors.DNSInterceptorOriginRecords>({ 4: { ips: [{ address: '127.0.0.1', ttl: 300, family: 4 }] }, 6: null })\n"
  },
  {
    "path": "test/types/env-http-proxy-agent.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable } from 'tsd'\nimport { EnvHttpProxyAgent, setGlobalDispatcher, getGlobalDispatcher, Dispatcher } from '../..'\n\nexpectAssignable<EnvHttpProxyAgent>(new EnvHttpProxyAgent())\nexpectAssignable<EnvHttpProxyAgent>(new EnvHttpProxyAgent({ httpProxy: 'http://localhost:8080', httpsProxy: 'http://localhost:8443', noProxy: 'localhost' }))\n\n{\n  const agent = new EnvHttpProxyAgent()\n  expectAssignable<void>(setGlobalDispatcher(agent))\n  expectAssignable<EnvHttpProxyAgent>(getGlobalDispatcher())\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: '', path: '', method: 'GET' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(agent.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }))\n  expectAssignable<void>(agent.request({ origin: '', path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<void>(agent.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(agent.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<void>(agent.stream(\n    { origin: '', path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(agent.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n\n  // pipeline\n  expectAssignable<Duplex>(agent.pipeline({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(agent.pipeline({ origin: '', path: '', method: 'GET', onInfo: (info) => {} }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(agent.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(agent.upgrade({ path: '' }))\n  expectAssignable<void>(agent.upgrade({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.UpgradeData>(data)\n  }))\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(agent.connect({ origin: '', path: '' }))\n  expectAssignable<Promise<Dispatcher.ConnectData>>(agent.connect({ origin: new URL('http://localhost'), path: '' }))\n  expectAssignable<void>(agent.connect({ origin: '', path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n  expectAssignable<void>(agent.connect({ origin: new URL('http://localhost'), path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(agent.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(agent.close())\n  expectAssignable<void>(agent.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(agent.destroy())\n  expectAssignable<Promise<void>>(agent.destroy(new Error()))\n  expectAssignable<Promise<void>>(agent.destroy(null))\n  expectAssignable<void>(agent.destroy(() => {}))\n  expectAssignable<void>(agent.destroy(new Error(), () => {}))\n  expectAssignable<void>(agent.destroy(null, () => {}))\n}\n"
  },
  {
    "path": "test/types/errors.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { errors } from '../..'\nimport Client from '../../types/client'\nimport { IncomingHttpHeaders } from '../../types/header'\n\nexpectAssignable<errors.UndiciError>(new errors.UndiciError())\nexpectAssignable<string>(new errors.UndiciError().name)\nexpectAssignable<string>(new errors.UndiciError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.ConnectTimeoutError())\nexpectAssignable<errors.ConnectTimeoutError>(new errors.ConnectTimeoutError())\nexpectAssignable<'ConnectTimeoutError'>(new errors.ConnectTimeoutError().name)\nexpectAssignable<'UND_ERR_CONNECT_TIMEOUT'>(new errors.ConnectTimeoutError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.HeadersTimeoutError())\nexpectAssignable<errors.HeadersTimeoutError>(new errors.HeadersTimeoutError())\nexpectAssignable<'HeadersTimeoutError'>(new errors.HeadersTimeoutError().name)\nexpectAssignable<'UND_ERR_HEADERS_TIMEOUT'>(new errors.HeadersTimeoutError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.HeadersOverflowError())\nexpectAssignable<errors.HeadersOverflowError>(new errors.HeadersOverflowError())\nexpectAssignable<'HeadersOverflowError'>(new errors.HeadersOverflowError().name)\nexpectAssignable<'UND_ERR_HEADERS_OVERFLOW'>(new errors.HeadersOverflowError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.BodyTimeoutError())\nexpectAssignable<errors.BodyTimeoutError>(new errors.BodyTimeoutError())\nexpectAssignable<'BodyTimeoutError'>(new errors.BodyTimeoutError().name)\nexpectAssignable<'UND_ERR_BODY_TIMEOUT'>(new errors.BodyTimeoutError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.ResponseError('', 0, {}))\nexpectAssignable<errors.ResponseError>(new errors.ResponseError('', 0, {}))\nexpectAssignable<'ResponseError'>(new errors.ResponseError('', 0, {}).name)\nexpectAssignable<'UND_ERR_RESPONSE'>(new errors.ResponseError('', 0, {}).code)\nexpectAssignable<number>(new errors.ResponseError('', 0, {}).statusCode)\nexpectAssignable<IncomingHttpHeaders | string[] | null>(new errors.ResponseError('', 0, {}).headers)\nexpectAssignable<null | Record<string, any> | string>(new errors.ResponseError('', 0, {}).body)\n\nexpectAssignable<errors.UndiciError>(new errors.InvalidArgumentError())\nexpectAssignable<errors.InvalidArgumentError>(new errors.InvalidArgumentError())\nexpectAssignable<'InvalidArgumentError'>(new errors.InvalidArgumentError().name)\nexpectAssignable<'UND_ERR_INVALID_ARG'>(new errors.InvalidArgumentError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.InvalidReturnValueError())\nexpectAssignable<errors.InvalidReturnValueError>(new errors.InvalidReturnValueError())\nexpectAssignable<'InvalidReturnValueError'>(new errors.InvalidReturnValueError().name)\nexpectAssignable<'UND_ERR_INVALID_RETURN_VALUE'>(new errors.InvalidReturnValueError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.RequestAbortedError())\nexpectAssignable<errors.RequestAbortedError>(new errors.RequestAbortedError())\nexpectAssignable<'AbortError'>(new errors.RequestAbortedError().name)\nexpectAssignable<'UND_ERR_ABORTED'>(new errors.RequestAbortedError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.InformationalError())\nexpectAssignable<errors.InformationalError>(new errors.InformationalError())\nexpectAssignable<'InformationalError'>(new errors.InformationalError().name)\nexpectAssignable<'UND_ERR_INFO'>(new errors.InformationalError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.RequestContentLengthMismatchError())\nexpectAssignable<errors.RequestContentLengthMismatchError>(new errors.RequestContentLengthMismatchError())\nexpectAssignable<'RequestContentLengthMismatchError'>(new errors.RequestContentLengthMismatchError().name)\nexpectAssignable<'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'>(new errors.RequestContentLengthMismatchError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.ResponseContentLengthMismatchError())\nexpectAssignable<errors.ResponseContentLengthMismatchError>(new errors.ResponseContentLengthMismatchError())\nexpectAssignable<'ResponseContentLengthMismatchError'>(new errors.ResponseContentLengthMismatchError().name)\nexpectAssignable<'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'>(new errors.ResponseContentLengthMismatchError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.ClientDestroyedError())\nexpectAssignable<errors.ClientDestroyedError>(new errors.ClientDestroyedError())\nexpectAssignable<'ClientDestroyedError'>(new errors.ClientDestroyedError().name)\nexpectAssignable<'UND_ERR_DESTROYED'>(new errors.ClientDestroyedError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.ClientClosedError())\nexpectAssignable<errors.ClientClosedError>(new errors.ClientClosedError())\nexpectAssignable<'ClientClosedError'>(new errors.ClientClosedError().name)\nexpectAssignable<'UND_ERR_CLOSED'>(new errors.ClientClosedError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.SocketError())\nexpectAssignable<errors.SocketError>(new errors.SocketError())\nexpectAssignable<'SocketError'>(new errors.SocketError().name)\nexpectAssignable<'UND_ERR_SOCKET'>(new errors.SocketError().code)\nexpectAssignable<Client.SocketInfo | null>(new errors.SocketError().socket)\n\nexpectAssignable<errors.UndiciError>(new errors.NotSupportedError())\nexpectAssignable<errors.NotSupportedError>(new errors.NotSupportedError())\nexpectAssignable<'NotSupportedError'>(new errors.NotSupportedError().name)\nexpectAssignable<'UND_ERR_NOT_SUPPORTED'>(new errors.NotSupportedError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.BalancedPoolMissingUpstreamError())\nexpectAssignable<errors.BalancedPoolMissingUpstreamError>(new errors.BalancedPoolMissingUpstreamError())\nexpectAssignable<'MissingUpstreamError'>(new errors.BalancedPoolMissingUpstreamError().name)\nexpectAssignable<'UND_ERR_BPL_MISSING_UPSTREAM'>(new errors.BalancedPoolMissingUpstreamError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.HTTPParserError())\nexpectAssignable<errors.HTTPParserError>(new errors.HTTPParserError())\nexpectAssignable<'HTTPParserError'>(new errors.HTTPParserError().name)\n\nexpectAssignable<errors.UndiciError>(new errors.ResponseExceededMaxSizeError())\nexpectAssignable<errors.ResponseExceededMaxSizeError>(new errors.ResponseExceededMaxSizeError())\nexpectAssignable<'ResponseExceededMaxSizeError'>(new errors.ResponseExceededMaxSizeError().name)\nexpectAssignable<'UND_ERR_RES_EXCEEDED_MAX_SIZE'>(new errors.ResponseExceededMaxSizeError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.RequestRetryError('', 0))\nexpectAssignable<errors.RequestRetryError>(new errors.RequestRetryError('', 0))\nexpectAssignable<'RequestRetryError'>(new errors.RequestRetryError('', 0).name)\nexpectAssignable<'UND_ERR_REQ_RETRY'>(new errors.RequestRetryError('', 0).code)\n\nexpectAssignable<errors.UndiciError>(new errors.SecureProxyConnectionError())\nexpectAssignable<errors.SecureProxyConnectionError>(new errors.SecureProxyConnectionError())\nexpectAssignable<'SecureProxyConnectionError'>(new errors.SecureProxyConnectionError().name)\nexpectAssignable<'UND_ERR_PRX_TLS'>(new errors.SecureProxyConnectionError().code)\n\nexpectAssignable<errors.UndiciError>(new errors.MaxOriginsReachedError())\nexpectAssignable<errors.MaxOriginsReachedError>(new errors.MaxOriginsReachedError())\nexpectAssignable<'MaxOriginsReachedError'>(new errors.MaxOriginsReachedError().name)\nexpectAssignable<'UND_ERR_MAX_ORIGINS_REACHED'>(new errors.MaxOriginsReachedError().code)\n\n{\n  // @ts-ignore\n  function f (): errors.HeadersTimeoutError | errors.ConnectTimeoutError { }\n\n  const e = f()\n\n  if (e.code === 'UND_ERR_HEADERS_TIMEOUT') {\n    expectAssignable<errors.HeadersTimeoutError>(e)\n  } else if (e.code === 'UND_ERR_CONNECT_TIMEOUT') {\n    expectAssignable<errors.ConnectTimeoutError>(e)\n  }\n}\n"
  },
  {
    "path": "test/types/event-source-d.ts",
    "content": "import { URL } from 'node:url'\nimport { expectType, expectAssignable } from 'tsd'\n\nimport { EventSource, EventSourceInit, Dispatcher } from '../../'\n\ndeclare const eventSource: EventSource\ndeclare const agent: Dispatcher\n\nexpectType<() => void>(eventSource.close)\nexpectType<string>(eventSource.url)\nexpectType<boolean>(eventSource.withCredentials)\nexpectType<0 | 1 | 2>(eventSource.readyState)\n\nexpectType<EventSource>(new EventSource('https://example.com'))\nexpectType<EventSource>(new EventSource(new URL('https://example.com')))\nexpectType<EventSource>(new EventSource('https://example.com', {}))\nexpectType<EventSource>(new EventSource('https://example.com', {\n  withCredentials: true\n}))\n\nexpectAssignable<EventSourceInit>({ dispatcher: agent })\nexpectAssignable<EventSourceInit>({ withCredentials: true })\nexpectAssignable<EventSourceInit>({})\n"
  },
  {
    "path": "test/types/fetch.test-d.ts",
    "content": "import { URL } from 'node:url'\nimport { Blob } from 'node:buffer'\nimport { ReadableStream } from 'node:stream/web'\nimport { expectType, expectError, expectAssignable, expectNotAssignable } from 'tsd'\nimport {\n  Agent,\n  BodyInit,\n  fetch,\n  FormData,\n  Headers,\n  HeadersInit,\n  SpecIterableIterator,\n  Request,\n  RequestCache,\n  RequestCredentials,\n  RequestDestination,\n  RequestInit,\n  RequestMode,\n  RequestRedirect,\n  Response,\n  ResponseInit,\n  ResponseType,\n  ReferrerPolicy,\n  Dispatcher\n} from '../..'\n\nconst requestInit: RequestInit = {}\nconst responseInit: ResponseInit = { status: 200, statusText: 'OK' }\nconst requestInit2: RequestInit = {\n  dispatcher: new Agent()\n}\nconst requestInit3: RequestInit = {}\n// Test assignment. See https://github.com/whatwg/fetch/issues/1445\nrequestInit3.credentials = 'include'\nconst requestInit4: RequestInit = { body: null }\n\ndeclare const request: Request\ndeclare const headers: Headers\ndeclare const response: Response\n\nexpectType<BodyInit | undefined>(requestInit.body)\nexpectType<RequestCache | undefined>(requestInit.cache)\nexpectType<RequestCredentials | undefined>(requestInit.credentials)\nexpectType<HeadersInit | undefined>(requestInit.headers)\nexpectType<string | undefined>(requestInit.integrity)\nexpectType<boolean | undefined>(requestInit.keepalive)\nexpectType<string | undefined>(requestInit.method)\nexpectType<RequestMode | undefined>(requestInit.mode)\nexpectType<RequestRedirect | undefined>(requestInit.redirect)\nexpectType<string | undefined>(requestInit.referrer)\nexpectType<ReferrerPolicy | undefined>(requestInit.referrerPolicy)\nexpectType<AbortSignal | null | undefined>(requestInit.signal)\nexpectType<null | undefined>(requestInit.window)\n\nexpectType<Dispatcher | undefined>(requestInit2.dispatcher)\n\nexpectType<BodyInit | undefined>(requestInit4.body)\n\nexpectType<number | undefined>(responseInit.status)\nexpectType<string | undefined>(responseInit.statusText)\nexpectType<HeadersInit | undefined>(responseInit.headers)\n\nexpectType<Headers>(new Headers())\nexpectType<Headers>(new Headers({}))\nexpectType<Headers>(new Headers([]))\nexpectType<Headers>(new Headers(headers))\nexpectType<Headers>(new Headers(undefined))\n\nexpectAssignable<HeadersInit>({ a: 'b' } as Record<string, string>)\nexpectAssignable<HeadersInit>({ 'content-type': 'application/gzip' } satisfies HeadersInit)\nexpectAssignable<HeadersInit>({ 'Content-Type': 'nonstandard/mime' } satisfies HeadersInit)\nexpectNotAssignable<HeadersInit>([['1', '2', '3']])\n\nexpectType<Request>(new Request(request))\nexpectType<Request>(new Request('https://example.com'))\nexpectType<Request>(new Request(new URL('https://example.com')))\nexpectType<Request>(new Request(request, requestInit))\nexpectType<Request>(new Request('https://example.com', requestInit))\nexpectType<Request>(new Request(new URL('https://example.com'), requestInit))\n\nexpectType<Promise<Response>>(fetch(request))\nexpectType<Promise<Response>>(fetch('https://example.com'))\nexpectType<Promise<Response>>(fetch(new URL('https://example.com')))\nexpectType<Promise<Response>>(fetch(request, requestInit))\nexpectType<Promise<Response>>(fetch('https://example.com', requestInit))\nexpectType<Promise<Response>>(fetch(new URL('https://example.com'), requestInit))\n\nexpectType<Response>(new Response())\nexpectType<Response>(new Response(null))\nexpectType<Response>(new Response('string'))\nexpectType<Response>(new Response(new Blob([])))\nexpectType<Response>(new Response(new FormData()))\nexpectType<Response>(new Response(new Int8Array()))\nexpectType<Response>(new Response(new Uint8Array()))\nexpectType<Response>(new Response(new Uint8ClampedArray()))\nexpectType<Response>(new Response(new Int16Array()))\nexpectType<Response>(new Response(new Uint16Array()))\nexpectType<Response>(new Response(new Int32Array()))\nexpectType<Response>(new Response(new Uint32Array()))\nexpectType<Response>(new Response(new Float32Array()))\nexpectType<Response>(new Response(new Float64Array()))\nexpectType<Response>(new Response(new BigInt64Array()))\nexpectType<Response>(new Response(new BigUint64Array()))\nexpectType<Response>(new Response(new ArrayBuffer(0)))\nexpectType<Response>(new Response(null, responseInit))\nexpectType<Response>(new Response('string', responseInit))\nexpectType<Response>(new Response(new Blob([]), responseInit))\nexpectType<Response>(new Response(new FormData(), responseInit))\nexpectType<Response>(new Response(new Int8Array(), responseInit))\nexpectType<Response>(new Response(new Uint8Array(), responseInit))\nexpectType<Response>(new Response(new Uint8ClampedArray(), responseInit))\nexpectType<Response>(new Response(new Int16Array(), responseInit))\nexpectType<Response>(new Response(new Uint16Array(), responseInit))\nexpectType<Response>(new Response(new Int32Array(), responseInit))\nexpectType<Response>(new Response(new Uint32Array(), responseInit))\nexpectType<Response>(new Response(new Float32Array(), responseInit))\nexpectType<Response>(new Response(new Float64Array(), responseInit))\nexpectType<Response>(new Response(new BigInt64Array(), responseInit))\nexpectType<Response>(new Response(new BigUint64Array(), responseInit))\nexpectType<Response>(new Response(new ArrayBuffer(0), responseInit))\nexpectType<Response>(Response.error())\nexpectType<Response>(Response.json({ a: 'b' }))\nexpectType<Response>(Response.json({}, { status: 200 }))\nexpectType<Response>(Response.json({}, { statusText: 'OK' }))\nexpectType<Response>(Response.json({}, { headers: {} }))\nexpectType<Response>(Response.json(null))\nexpectType<Response>(Response.redirect('https://example.com', 301))\nexpectType<Response>(Response.redirect('https://example.com', 302))\nexpectType<Response>(Response.redirect('https://example.com', 303))\nexpectType<Response>(Response.redirect('https://example.com', 307))\nexpectType<Response>(Response.redirect('https://example.com', 308))\nexpectError(Response.redirect('https://example.com', NaN))\nexpectError(Response.json())\nexpectError(Response.json(null, 3))\n\nexpectType<void>(headers.append('key', 'value'))\nexpectType<void>(headers.delete('key'))\nexpectType<string | null>(headers.get('key'))\nexpectType<boolean>(headers.has('key'))\nexpectType<void>(headers.set('key', 'value'))\nexpectType<SpecIterableIterator<string>>(headers.keys())\nexpectType<SpecIterableIterator<string>>(headers.values())\nexpectType<SpecIterableIterator<[string, string]>>(headers.entries())\n\nexpectType<RequestCache>(request.cache)\nexpectType<RequestCredentials>(request.credentials)\nexpectType<RequestDestination>(request.destination)\nexpectType<Headers>(request.headers)\nexpectType<string>(request.integrity)\nexpectType<string>(request.method)\nexpectType<RequestMode>(request.mode)\nexpectType<RequestRedirect>(request.redirect)\nexpectType<ReferrerPolicy>(request.referrerPolicy)\nexpectType<string>(request.url)\nexpectType<boolean>(request.keepalive)\nexpectType<AbortSignal>(request.signal)\nexpectType<boolean>(request.bodyUsed)\nexpectType<Promise<ArrayBuffer>>(request.arrayBuffer())\nexpectType<Promise<Blob>>(request.blob())\nexpectType<Promise<FormData>>(request.formData())\nexpectType<Promise<unknown>>(request.json())\nexpectType<Promise<string>>(request.text())\nexpectType<Request>(request.clone())\n\nexpectType<Headers>(response.headers)\nexpectType<boolean>(response.ok)\nexpectType<number>(response.status)\nexpectType<string>(response.statusText)\nexpectType<ResponseType>(response.type)\nexpectType<string>(response.url)\nexpectType<boolean>(response.redirected)\nexpectType<ReadableStream | null>(response.body)\nexpectType<boolean>(response.bodyUsed)\nexpectType<Promise<ArrayBuffer>>(response.arrayBuffer())\nexpectType<Promise<Blob>>(response.blob())\nexpectType<Promise<FormData>>(response.formData())\nexpectType<Promise<Uint8Array>>(response.bytes())\nexpectType<Promise<unknown>>(response.json())\nexpectType<Promise<string>>(response.text())\nexpectType<Response>(response.clone())\n\nexpectType<Request>(new Request('https://example.com', { body: 'Hello, world', duplex: 'half' }))\nexpectAssignable<RequestInit>({ duplex: 'half' })\nexpectNotAssignable<RequestInit>({ duplex: 'not valid' })\n\nexpectType<string[]>(headers.getSetCookie())\n\nexpectType<Request>(new Request('https://example.com', request))\n"
  },
  {
    "path": "test/types/formdata.test-d.ts",
    "content": "import { Blob, File } from 'node:buffer'\nimport { Readable } from 'node:stream'\nimport { expectAssignable, expectType } from 'tsd'\nimport { FormData, SpecIterableIterator } from '../..'\nimport Dispatcher from '../../types/dispatcher'\n\ndeclare const dispatcherOptions: Dispatcher.DispatchOptions\n\ndeclare const blob: Blob\nconst formData = new FormData()\nexpectType<FormData>(formData)\n\nexpectType<void>(formData.append('key', 'value'))\nexpectType<void>(formData.append('key', blob))\nexpectType<void>(formData.set('key', 'value'))\nexpectType<void>(formData.set('key', blob))\nexpectType<File | string | null>(formData.get('key'))\nexpectType<File | string | null>(formData.get('key'))\nexpectType<Array<File | string>>(formData.getAll('key'))\nexpectType<Array<File | string>>(formData.getAll('key'))\nexpectType<boolean>(formData.has('key'))\nexpectType<void>(formData.delete('key'))\nexpectAssignable<SpecIterableIterator<string>>(formData.keys())\nexpectAssignable<SpecIterableIterator<File | string>>(formData.values())\nexpectAssignable<SpecIterableIterator<[string, File | string]>>(formData.entries())\nexpectAssignable<SpecIterableIterator<[string, File | string]>>(formData[Symbol.iterator]())\nexpectAssignable<string | Buffer | Uint8Array | FormData | Readable | undefined | null>(dispatcherOptions.body)\n"
  },
  {
    "path": "test/types/global-dispatcher.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { setGlobalDispatcher, Dispatcher, getGlobalDispatcher } from '../..'\n\n{\n  expectAssignable<void>(setGlobalDispatcher(new Dispatcher()))\n  class CustomDispatcher extends Dispatcher {}\n  expectAssignable<void>(setGlobalDispatcher(new CustomDispatcher()))\n}\n\nexpectAssignable<Dispatcher>(getGlobalDispatcher())\n"
  },
  {
    "path": "test/types/header.test-d.ts",
    "content": "import { IncomingHttpHeaders as CoreIncomingHttpHeaders } from 'node:http'\nimport { expectAssignable, expectNotAssignable } from 'tsd'\nimport { IncomingHttpHeaders } from '../../types/header'\n\nconst headers = {\n  authorization: undefined,\n  'content-type': 'application/json'\n} satisfies CoreIncomingHttpHeaders\n\nexpectAssignable<IncomingHttpHeaders>(headers)\n\n// It is why we do not need to add ` | null` to `IncomingHttpHeaders`:\nexpectNotAssignable<CoreIncomingHttpHeaders>({\n  authorization: null,\n  'content-type': 'application/json'\n})\n"
  },
  {
    "path": "test/types/index.test-d.ts",
    "content": "import { expectAssignable, expectType } from 'tsd'\nimport Undici, {\n  Pool,\n  Client,\n  errors,\n  fetch,\n  Interceptable,\n  RedirectHandler,\n  Headers,\n  Response,\n  Request,\n  FormData,\n  SnapshotAgent,\n  install,\n  cacheStores\n} from '../..'\nimport Dispatcher from '../../types/dispatcher'\nimport CacheInterceptor from '../../types/cache-interceptor'\n\nexpectAssignable<Pool>(new Undici.Pool('', {}))\nexpectAssignable<Client>(new Undici.Client('', {}))\nexpectAssignable<Interceptable>(new Undici.MockAgent().get(''))\nexpectAssignable<SnapshotAgent>(new Undici.SnapshotAgent())\nexpectAssignable<typeof errors>(Undici.errors)\nexpectAssignable<typeof fetch>(Undici.fetch)\nexpectAssignable<typeof Headers>(Undici.Headers)\nexpectAssignable<typeof Response>(Undici.Response)\nexpectAssignable<typeof Request>(Undici.Request)\nexpectAssignable<typeof FormData>(Undici.FormData)\nexpectAssignable<Dispatcher.DispatcherComposeInterceptor>(Undici.interceptors.dump())\nexpectAssignable<Dispatcher.DispatcherComposeInterceptor>(Undici.interceptors.redirect())\nexpectAssignable<Dispatcher.DispatcherComposeInterceptor>(Undici.interceptors.retry())\nexpectAssignable<Dispatcher.DispatcherComposeInterceptor>(Undici.interceptors.decompress())\nexpectAssignable<Dispatcher.DispatcherComposeInterceptor>(Undici.interceptors.cache())\nexpectAssignable<CacheInterceptor.CacheStore>(new Undici.cacheStores.MemoryCacheStore())\nexpectAssignable<CacheInterceptor.CacheStore>(new Undici.cacheStores.SqliteCacheStore())\nexpectAssignable<CacheInterceptor.CacheStore>(new cacheStores.MemoryCacheStore())\nexpectAssignable<CacheInterceptor.CacheStore>(new cacheStores.SqliteCacheStore())\n\nconst dispatcher = new Dispatcher()\nconst handler: Dispatcher.DispatchHandler = {}\n\nconst redirectHandler = new Undici.RedirectHandler(dispatcher.dispatch, 10, {\n  path: '/', method: 'GET'\n}, handler, false) as RedirectHandler\nexpectAssignable<RedirectHandler>(redirectHandler)\n\nexpectType<() => void>(install)\nexpectType<() => void>(Undici.install)\n"
  },
  {
    "path": "test/types/mock-agent.test-d.ts",
    "content": "import { expectAssignable, expectType } from 'tsd'\nimport { Agent, Dispatcher, MockAgent, MockClient, MockPool, setGlobalDispatcher } from '../..'\nimport { MockInterceptor } from '../../types/mock-interceptor'\nimport ProxyAgent from '../../types/proxy-agent'\nimport EnvHttpProxyAgent from '../../types/env-http-proxy-agent'\nimport RetryAgent from '../../types/retry-agent'\n\nimport MockDispatch = MockInterceptor.MockDispatch\n\nexpectAssignable<MockAgent>(new MockAgent())\nexpectAssignable<MockAgent>(new MockAgent({}))\n\n{\n  const mockAgent = new MockAgent()\n  expectAssignable<void>(setGlobalDispatcher(mockAgent))\n\n  // get\n  expectAssignable<MockPool>(mockAgent.get(''))\n  // eslint-disable-next-line prefer-regex-literals\n  expectAssignable<MockPool>(mockAgent.get(new RegExp('')))\n  expectAssignable<MockPool>(mockAgent.get((origin) => {\n    expectAssignable<string>(origin)\n    return true\n  }))\n  expectAssignable<Dispatcher>(mockAgent.get(''))\n\n  // close\n  expectAssignable<Promise<void>>(mockAgent.close())\n\n  // deactivate\n  expectAssignable<void>(mockAgent.deactivate())\n\n  // activate\n  expectAssignable<void>(mockAgent.activate())\n\n  // enableNetConnect\n  expectAssignable<void>(mockAgent.enableNetConnect())\n  expectAssignable<void>(mockAgent.enableNetConnect(''))\n  // eslint-disable-next-line prefer-regex-literals\n  expectAssignable<void>(mockAgent.enableNetConnect(new RegExp('')))\n  expectAssignable<void>(mockAgent.enableNetConnect((host) => {\n    expectAssignable<string>(host)\n    return true\n  }))\n\n  // disableNetConnect\n  expectAssignable<void>(mockAgent.disableNetConnect())\n\n  // dispatch\n  expectAssignable<boolean>(mockAgent.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n\n  // intercept\n  expectAssignable<MockInterceptor>((mockAgent.get('foo')).intercept({ path: '', method: 'GET' }))\n}\n\n{\n  const mockAgent = new MockAgent({ connections: 1 })\n  expectAssignable<void>(setGlobalDispatcher(mockAgent))\n  expectAssignable<MockClient>(mockAgent.get(''))\n}\n\n{\n  const agent = new Agent()\n  const mockAgent = new MockAgent({ agent })\n  expectAssignable<void>(setGlobalDispatcher(mockAgent))\n  expectAssignable<MockPool>(mockAgent.get(''))\n}\n\n{\n  interface PendingInterceptor extends MockDispatch {\n    origin: string;\n  }\n\n  const agent = new MockAgent({ agent: new Agent() })\n  expectType<() => PendingInterceptor[]>(agent.pendingInterceptors)\n  expectType<(options?: {\n    pendingInterceptorsFormatter?: {\n      format(pendingInterceptors: readonly PendingInterceptor[]): string;\n    }\n  }) => void>(agent.assertNoPendingInterceptors)\n}\n\n// issue #3444\n// ProxyAgent, EnvHttpProxyAgent, RetryAgent should be assignable to the agent\n// option of MockAgent\n\nexpectType<MockAgent>(new MockAgent({\n  agent: new ProxyAgent({\n    uri: 'http://localhost:3000'\n  })\n}))\nexpectType<MockAgent>(new MockAgent({\n  agent: new EnvHttpProxyAgent()\n}))\nexpectType<MockAgent>(new MockAgent({\n  agent: new RetryAgent(new Agent())\n}))\nexpectType<MockAgent>(new MockAgent({\n  acceptNonStandardSearchParameters: true\n}))\nexpectType<MockAgent>(new MockAgent({\n  acceptNonStandardSearchParameters: false\n}))\nexpectType<MockAgent>(new MockAgent({\n  acceptNonStandardSearchParameters: undefined\n}))\n"
  },
  {
    "path": "test/types/mock-call-history.test-d.ts",
    "content": "import { expectType } from 'tsd'\nimport { MockAgent, MockCallHistory, MockCallHistoryLog } from '../..'\n\n{\n  const mockAgent = new MockAgent()\n  expectType<MockCallHistory | undefined>(mockAgent.getCallHistory())\n  expectType<void>(mockAgent.clearCallHistory())\n  expectType<MockAgent>(mockAgent.enableCallHistory())\n  expectType<MockAgent>(mockAgent.disableCallHistory())\n}\n\n{\n  const mockAgent = new MockAgent({ enableCallHistory: true })\n  expectType<MockCallHistoryLog | undefined>(mockAgent.getCallHistory()?.firstCall())\n  expectType<MockCallHistoryLog | undefined>(mockAgent.getCallHistory()?.lastCall())\n  expectType<MockCallHistoryLog | undefined>(mockAgent.getCallHistory()?.nthCall(1))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.calls())\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByFullUrl(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByHash(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByHost(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByMethod(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByOrigin(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByPath(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByPort(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCallsByProtocol(''))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCalls((log) => log.path === '/'))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCalls(/path->\\//))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCalls({ method: 'POST' }))\n  expectType<Array<MockCallHistoryLog> | undefined>(mockAgent.getCallHistory()?.filterCalls({ method: 'POST' }, { operator: 'AND' }))\n\n  const callHistory = mockAgent.getCallHistory()\n\n  if (callHistory !== undefined) {\n    expectType<Array<MockCallHistoryLog>>([...callHistory])\n    expectType<Set<MockCallHistoryLog>>(new Set(callHistory))\n\n    for (const log of callHistory) {\n      expectType<MockCallHistoryLog>(log)\n      expectType<string | null | undefined>(log.body)\n      expectType<string>(log.fullUrl)\n      expectType<string>(log.hash)\n      expectType<Record<string, string | Array<string>> | null | undefined>(log.headers)\n      expectType<string>(log.host)\n      expectType<string>(log.method)\n      expectType<string>(log.origin)\n      expectType<string>(log.path)\n      expectType<string>(log.port)\n      expectType<string>(log.protocol)\n      expectType<Record<string, string>>(log.searchParams)\n      expectType<Map<MockCallHistoryLog.MockCallHistoryLogProperties, string | Record<string, string | string[]> | null | undefined>>(log.toMap())\n      expectType<string>(log.toString())\n    }\n  }\n  expectType<void | undefined>(mockAgent.getCallHistory()?.clear())\n}\n"
  },
  {
    "path": "test/types/mock-client.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { MockAgent, MockClient } from '../..'\nimport { MockInterceptor } from '../../types/mock-interceptor'\n\n{\n  const mockClient: MockClient = new MockAgent({ connections: 1 }).get('')\n\n  // intercept\n  expectAssignable<MockInterceptor>(mockClient.intercept({ path: '', method: 'GET' }))\n  expectAssignable<MockInterceptor>(mockClient.intercept({ path: '', method: 'GET', body: '', headers: { 'User-Agent': '' } }))\n  expectAssignable<MockInterceptor>(mockClient.intercept({ path: '', method: 'GET', query: { id: 1 } }))\n  // eslint-disable-next-line prefer-regex-literals\n  expectAssignable<MockInterceptor>(mockClient.intercept({ path: new RegExp(''), method: new RegExp(''), body: new RegExp(''), headers: { 'User-Agent': new RegExp('') } }))\n  expectAssignable<MockInterceptor>(mockClient.intercept({\n    path: (path) => {\n      expectAssignable<string>(path)\n      return true\n    },\n    method: (method) => {\n      expectAssignable<string>(method)\n      return true\n    },\n    body: (body) => {\n      expectAssignable<string>(body)\n      return true\n    },\n    headers: {\n      'User-Agent': (header) => {\n        expectAssignable<string>(header)\n        return true\n      }\n    }\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(mockClient.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(mockClient.close())\n\n  // cleanMocks\n  expectAssignable<void>(mockClient.cleanMocks())\n}\n\nexpectAssignable<MockClient>(new MockClient('', { agent: new MockAgent({ connections: 1 }) }))\n"
  },
  {
    "path": "test/types/mock-errors.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { mockErrors, errors } from '../..'\n\nexpectAssignable<errors.UndiciError>(new mockErrors.MockNotMatchedError())\nexpectAssignable<mockErrors.MockNotMatchedError>(new mockErrors.MockNotMatchedError())\nexpectAssignable<mockErrors.MockNotMatchedError>(new mockErrors.MockNotMatchedError('kaboom'))\nexpectAssignable<'MockNotMatchedError'>(new mockErrors.MockNotMatchedError().name)\nexpectAssignable<'UND_MOCK_ERR_MOCK_NOT_MATCHED'>(new mockErrors.MockNotMatchedError().code)\n\n{\n  // @ts-ignore\n  function f (): mockErrors.MockNotMatchedError { }\n\n  const e = f()\n\n  if (e.code === 'UND_MOCK_ERR_MOCK_NOT_MATCHED') {\n    expectAssignable<mockErrors.MockNotMatchedError>(e)\n  }\n}\n"
  },
  {
    "path": "test/types/mock-interceptor.test-d.ts",
    "content": "import { expectAssignable, expectType } from 'tsd'\nimport { MockAgent, MockPool, BodyInit, Dispatcher } from '../..'\nimport { MockInterceptor, MockScope } from '../../types/mock-interceptor'\n\ndeclare const mockResponseCallbackOptions: MockInterceptor.MockResponseCallbackOptions\n\nexpectAssignable<BodyInit | Dispatcher.DispatchOptions['body']>(mockResponseCallbackOptions.body)\n\n{\n  const mockPool: MockPool = new MockAgent().get('')\n  const mockInterceptor = mockPool.intercept({ path: '', method: 'GET' })\n  const mockInterceptorDefaultMethod = mockPool.intercept({ path: '' })\n\n  expectType<MockInterceptor>(mockInterceptorDefaultMethod)\n\n  // reply\n  expectAssignable<MockScope>(mockInterceptor.reply(200))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, ''))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, Buffer))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, {}))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, () => ({})))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, {}, {}))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, () => ({}), {}))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, {}, { headers: { foo: 'bar' } }))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, () => ({}), { headers: { foo: 'bar' } }))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, {}, { trailers: { foo: 'bar' } }))\n  expectAssignable<MockScope>(mockInterceptor.reply(200, () => ({}), { trailers: { foo: 'bar' } }))\n  expectAssignable<MockScope<{ foo: string }>>(mockInterceptor.reply<{ foo: string }>(200, { foo: 'bar' }))\n  expectAssignable<MockScope<{ foo: string }>>(mockInterceptor.reply<{ foo: string }>(200, () => ({ foo: 'bar' })))\n  expectAssignable<MockScope>(mockInterceptor.reply(() => ({ statusCode: 200, data: { foo: 'bar' } })))\n  expectAssignable<MockScope>(mockInterceptor.reply(() => ({\n    statusCode: 200,\n    data: { foo: 'bar' },\n    responseOptions: {\n      headers: { foo: 'bar' }\n    }\n  })))\n  expectAssignable<MockScope>(mockInterceptor.reply((options) => {\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions>(options)\n    return { statusCode: 200, data: { foo: 'bar' } }\n  }))\n  expectAssignable<MockScope>(mockInterceptor.reply(() => ({\n    statusCode: 200,\n    data: { foo: 'bar' },\n    responseOptions: {\n      trailers: { foo: 'bar' }\n    }\n  })))\n  mockInterceptor.reply((options) => {\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions['path']>(options.path)\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions['method']>(options.method)\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions['headers']>(options.headers)\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions['origin']>(options.origin)\n    expectAssignable<MockInterceptor.MockResponseCallbackOptions['body']>(options.body)\n    return { statusCode: 200, data: { foo: 'bar' } }\n  })\n\n  // replyWithError\n  class CustomError extends Error {\n    hello (): void {}\n  }\n  expectAssignable<MockScope>(mockInterceptor.replyWithError(new Error('')))\n  expectAssignable<MockScope>(mockInterceptor.replyWithError<CustomError>(new CustomError('')))\n\n  // defaultReplyHeaders\n  expectAssignable<MockInterceptor>(mockInterceptor.defaultReplyHeaders({ foo: 'bar' }))\n\n  // defaultReplyTrailers\n  expectAssignable<MockInterceptor>(mockInterceptor.defaultReplyTrailers({ foo: 'bar' }))\n\n  // replyContentLength\n  expectAssignable<MockInterceptor>(mockInterceptor.replyContentLength())\n}\n\n{\n  const mockPool: MockPool = new MockAgent().get('')\n  const mockScope = mockPool.intercept({ path: '', method: 'GET' }).reply(200)\n\n  // delay\n  expectAssignable<MockScope>(mockScope.delay(1))\n\n  // persist\n  expectAssignable<MockScope>(mockScope.persist())\n\n  // times\n  expectAssignable<MockScope>(mockScope.times(2))\n}\n\n{\n  const mockPool: MockPool = new MockAgent().get('')\n  mockPool.intercept({ path: '', method: 'GET', headers: () => true })\n  mockPool.intercept({ path: '', method: 'GET', headers: () => false })\n  mockPool.intercept({ path: '', method: 'GET', headers: (headers) => Object.keys(headers).includes('authorization') })\n}\n"
  },
  {
    "path": "test/types/mock-pool.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { MockAgent, MockPool } from '../..'\nimport { MockInterceptor } from '../../types/mock-interceptor'\n\n{\n  const mockPool: MockPool = new MockAgent({ connections: 1 }).get('')\n\n  // intercept\n  expectAssignable<MockInterceptor>(mockPool.intercept({ path: '', method: 'GET' }))\n  expectAssignable<MockInterceptor>(mockPool.intercept({ path: '', method: 'GET', body: '', headers: { 'User-Agent': '' } }))\n  // eslint-disable-next-line prefer-regex-literals\n  expectAssignable<MockInterceptor>(mockPool.intercept({ path: new RegExp(''), method: new RegExp(''), body: new RegExp(''), headers: { 'User-Agent': new RegExp('') } }))\n  expectAssignable<MockInterceptor>(mockPool.intercept({\n    path: (path) => {\n      expectAssignable<string>(path)\n      return true\n    },\n    method: (method) => {\n      expectAssignable<string>(method)\n      return true\n    },\n    body: (body) => {\n      expectAssignable<string>(body)\n      return true\n    },\n    headers: {\n      'User-Agent': (header) => {\n        expectAssignable<string>(header)\n        return true\n      }\n    }\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(mockPool.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(mockPool.close())\n\n  // cleanMocks\n  expectAssignable<void>(mockPool.cleanMocks())\n}\n\nexpectAssignable<MockPool>(new MockPool('', { agent: new MockAgent({ connections: 1 }) }))\n"
  },
  {
    "path": "test/types/pool.test-d.ts",
    "content": "import { Duplex, Readable, Writable } from 'node:stream'\nimport { expectAssignable, expectType } from 'tsd'\nimport { Dispatcher, Pool, Client } from '../..'\nimport { URL } from 'node:url'\n\nexpectAssignable<Pool>(new Pool(''))\nexpectAssignable<Pool>(new Pool('', {}))\nexpectAssignable<Pool>(new Pool(new URL('http://localhost'), {}))\nexpectAssignable<Pool>(new Pool('', { factory: () => new Dispatcher() }))\nexpectAssignable<Pool>(new Pool('', { factory: (origin, opts) => new Client(origin, opts) }))\nexpectAssignable<Pool>(new Pool('', { connections: 1 }))\n\n{\n  const pool = new Pool('', {})\n\n  // properties\n  expectAssignable<boolean>(pool.closed)\n  expectAssignable<boolean>(pool.destroyed)\n  expectAssignable<Pool.PoolStats>(pool.stats)\n\n  // request\n  expectAssignable<Promise<Dispatcher.ResponseData>>(pool.request({ origin: '', path: '', method: 'GET' }))\n  expectAssignable<Promise<Dispatcher.ResponseData>>(pool.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }))\n  expectAssignable<void>(pool.request({ origin: '', path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n  expectAssignable<void>(pool.request({ origin: new URL('http://localhost'), path: '', method: 'GET' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ResponseData>(data)\n  }))\n\n  // stream\n  expectAssignable<Promise<Dispatcher.StreamData>>(pool.stream({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<Promise<Dispatcher.StreamData>>(pool.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.StreamFactoryData>(data)\n    return new Writable()\n  }))\n  expectAssignable<void>(pool.stream(\n    { origin: '', path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n  expectAssignable<void>(pool.stream(\n    { origin: new URL('http://localhost'), path: '', method: 'GET' },\n    data => {\n      expectAssignable<Dispatcher.StreamFactoryData>(data)\n      return new Writable()\n    },\n    (err, data) => {\n      expectAssignable<Error | null>(err)\n      expectAssignable<Dispatcher.StreamData>(data)\n    }\n  ))\n\n  // pipeline\n  expectAssignable<Duplex>(pool.pipeline({ origin: '', path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n  expectAssignable<Duplex>(pool.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => {\n    expectAssignable<Dispatcher.PipelineHandlerData>(data)\n    return new Readable()\n  }))\n\n  // upgrade\n  expectAssignable<Promise<Dispatcher.UpgradeData>>(pool.upgrade({ path: '' }))\n  expectAssignable<void>(pool.upgrade({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.UpgradeData>(data)\n  }))\n\n  // connect\n  expectAssignable<Promise<Dispatcher.ConnectData>>(pool.connect({ path: '' }))\n  expectAssignable<void>(pool.connect({ path: '' }, (err, data) => {\n    expectAssignable<Error | null>(err)\n    expectAssignable<Dispatcher.ConnectData>(data)\n  }))\n\n  // dispatch\n  expectAssignable<boolean>(pool.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n  expectAssignable<boolean>(pool.dispatch({ origin: new URL('http://localhost'), path: '', method: 'GET' }, {}))\n\n  // close\n  expectAssignable<Promise<void>>(pool.close())\n  expectAssignable<void>(pool.close(() => {}))\n\n  // destroy\n  expectAssignable<Promise<void>>(pool.destroy())\n  expectAssignable<Promise<void>>(pool.destroy(new Error()))\n  expectAssignable<Promise<void>>(pool.destroy(null))\n  expectAssignable<void>(pool.destroy(() => {}))\n  expectAssignable<void>(pool.destroy(new Error(), () => {}))\n  expectAssignable<void>(pool.destroy(null, () => {}))\n\n  // stats\n  expectType<number>(pool.stats.connected)\n  expectType<number>(pool.stats.free)\n  expectType<number>(pool.stats.pending)\n  expectType<number>(pool.stats.queued)\n  expectType<number>(pool.stats.running)\n  expectType<number>(pool.stats.size)\n}\n"
  },
  {
    "path": "test/types/proxy-agent.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { URL } from 'node:url'\nimport { ProxyAgent, setGlobalDispatcher, getGlobalDispatcher, Agent, Pool } from '../..'\n\nexpectAssignable<ProxyAgent>(new ProxyAgent(''))\nexpectAssignable<ProxyAgent>(new ProxyAgent({ uri: '' }))\nexpectAssignable<ProxyAgent>(\n  new ProxyAgent({\n    connections: 1,\n    uri: '',\n    auth: '',\n    token: '',\n    factory: (_origin: URL, opts: Object) => new Agent(opts),\n    requestTls: {\n      ca: [''],\n      key: '',\n      cert: '',\n      servername: '',\n      timeout: 1\n    },\n    proxyTls: {\n      ca: [''],\n      key: '',\n      cert: '',\n      servername: '',\n      timeout: 1\n    },\n    clientFactory: (origin: URL, opts: object) => new Pool(origin, opts)\n  })\n)\n\n{\n  const proxyAgent = new ProxyAgent('')\n  expectAssignable<void>(setGlobalDispatcher(proxyAgent))\n  expectAssignable<ProxyAgent>(getGlobalDispatcher())\n\n  // close\n  expectAssignable<Promise<void>>(proxyAgent.close())\n\n  // dispatch\n  expectAssignable<boolean>(proxyAgent.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n}\n"
  },
  {
    "path": "test/types/readable.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport BodyReadable from '../../types/readable'\nimport { Blob } from 'node:buffer'\n\nexpectAssignable<BodyReadable>(new BodyReadable({\n  abort: () => null,\n  resume: () => null\n}))\n\n{\n  const readable = new BodyReadable({\n    abort: () => null,\n    resume: () => null\n  })\n\n  // dump\n  expectAssignable<Promise<void>>(readable.dump())\n  expectAssignable<Promise<void>>(readable.dump({ limit: 123 }))\n\n  // text\n  expectAssignable<Promise<string>>(readable.text())\n\n  // json\n  expectAssignable<Promise<unknown>>(readable.json())\n\n  // blob\n  expectAssignable<Promise<Blob>>(readable.blob())\n\n  // bytes\n  expectAssignable<Promise<Uint8Array>>(readable.bytes())\n\n  // arrayBuffer\n  expectAssignable<Promise<ArrayBuffer>>(readable.arrayBuffer())\n\n  // formData\n  expectAssignable<Promise<never>>(readable.formData())\n\n  // bodyUsed\n  expectAssignable<boolean>(readable.bodyUsed)\n\n  // body\n  expectAssignable<never | undefined>(readable.body)\n}\n"
  },
  {
    "path": "test/types/retry-agent.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { RetryAgent, Agent } from '../..'\n\nconst dispatcher = new Agent()\n\nexpectAssignable<RetryAgent>(new RetryAgent(dispatcher))\nexpectAssignable<RetryAgent>(new RetryAgent(dispatcher, { maxRetries: 5 }))\nexpectAssignable<RetryAgent>(new RetryAgent(dispatcher, { throwOnError: false }))\n\n{\n  const retryAgent = new RetryAgent(dispatcher)\n\n  // close\n  expectAssignable<Promise<void>>(retryAgent.close())\n\n  // dispatch\n  expectAssignable<boolean>(retryAgent.dispatch({ origin: '', path: '', method: 'GET' }, {}))\n}\n"
  },
  {
    "path": "test/types/retry-handler.test-d.ts",
    "content": "import { expectType, expectAssignable, expectNotAssignable } from 'tsd'\nimport { Dispatcher, RetryHandler } from '../..'\n\n// Test the basic structure of RetryCallback\nexpectType<RetryHandler.RetryCallback>((err, context, callback) => {\n  expectType<Error>(err)\n  expectType<{\n    state: RetryHandler.RetryState;\n    opts: Dispatcher.DispatchOptions & {\n      retryOptions?: RetryHandler.RetryOptions;\n    };\n  }>(context)\n  expectType<RetryHandler.OnRetryCallback>(callback)\n})\n\n// Test that RetryCallback returns void\nconst testCallback = (() => {}) as RetryHandler.RetryCallback\nconst testContext = {\n  state: {} as RetryHandler.RetryState,\n  opts: {} as Dispatcher.DispatchOptions & {\n    retryOptions?: RetryHandler.RetryOptions;\n  }\n}\n\nexpectType<void>(testCallback(new Error(), testContext, () => {}))\n\n// Test that the function is assignable to RetryCallback\nexpectAssignable<RetryHandler.RetryCallback>(testCallback)\n\n// Test that an incorrectly typed function is not assignable to RetryCallback\nexpectNotAssignable<RetryHandler.RetryCallback>((() => {}) as (\n  err: string,\n  context: number,\n  callback: boolean\n) => void)\n\n// Test the nested types\nconst contextTest: Parameters<RetryHandler.RetryCallback>[1] = {\n  state: {} as RetryHandler.RetryState,\n  opts: {\n    method: 'GET',\n    path: 'some-path',\n    retryOptions: {} as RetryHandler.RetryOptions\n  }\n}\nexpectType<RetryHandler.RetryState>(contextTest.state)\nexpectType<\n  Dispatcher.DispatchOptions & { retryOptions?: RetryHandler.RetryOptions }\n>(contextTest.opts)\n"
  },
  {
    "path": "test/types/snapshot-agent.test-d.ts",
    "content": "import { expectAssignable, expectType } from 'tsd'\nimport { Agent, Dispatcher, MockAgent, SnapshotAgent, setGlobalDispatcher } from '../..'\nimport { SnapshotRecorder } from '../../types/snapshot-agent'\n\n// Basic constructor tests\nexpectAssignable<SnapshotAgent>(new SnapshotAgent())\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({}))\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({ mode: 'record' }))\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({ mode: 'playback' }))\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({ mode: 'update' }))\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({ snapshotPath: './snapshots.json' }))\n\n// Constructor with basic options\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  maxSnapshots: 100,\n  autoFlush: true,\n  flushInterval: 5000\n}))\n\n// Constructor with matching options\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({\n  mode: 'playback',\n  snapshotPath: './snapshots.json',\n  matchHeaders: ['content-type', 'accept'],\n  ignoreHeaders: ['authorization', 'x-api-key'],\n  excludeHeaders: ['set-cookie', 'authorization'],\n  matchBody: true,\n  matchQuery: false,\n  caseSensitive: false\n}))\n\n// Constructor with all options combined\nexpectAssignable<SnapshotAgent>(new SnapshotAgent({\n  mode: 'update',\n  snapshotPath: './snapshots.json',\n  maxSnapshots: 50,\n  autoFlush: true,\n  flushInterval: 10000,\n  matchHeaders: ['content-type'],\n  ignoreHeaders: ['user-agent'],\n  excludeHeaders: ['cookie'],\n  matchBody: false,\n  matchQuery: true,\n  caseSensitive: true,\n  connections: 5, // MockAgent option\n  enableCallHistory: true // MockAgent option\n}))\n\n// SnapshotAgent extends MockAgent\nexpectAssignable<MockAgent>(new SnapshotAgent())\nexpectAssignable<Dispatcher>(new SnapshotAgent())\n\n{\n  const snapshotAgent = new SnapshotAgent()\n  expectAssignable<void>(setGlobalDispatcher(snapshotAgent))\n\n  // Original snapshot methods\n  expectType<Promise<void>>(snapshotAgent.saveSnapshots())\n  expectType<Promise<void>>(snapshotAgent.saveSnapshots('./custom.json'))\n  expectType<Promise<void>>(snapshotAgent.loadSnapshots())\n  expectType<Promise<void>>(snapshotAgent.loadSnapshots('./custom.json'))\n  expectType<SnapshotRecorder>(snapshotAgent.getRecorder())\n  expectType<SnapshotRecorder.SnapshotRecorderMode>(snapshotAgent.getMode())\n  expectType<void>(snapshotAgent.clearSnapshots())\n\n  // New snapshot management methods\n  expectType<void>(snapshotAgent.resetCallCounts())\n  expectType<boolean>(snapshotAgent.deleteSnapshot({}))\n  expectType<SnapshotRecorder.SnapshotInfo | null>(snapshotAgent.getSnapshotInfo({}))\n  expectType<void>(snapshotAgent.replaceSnapshots([]))\n\n  // Inherited MockAgent methods\n  expectType<Promise<void>>(snapshotAgent.close())\n  expectType<void>(snapshotAgent.deactivate())\n  expectType<void>(snapshotAgent.activate())\n  expectType<void>(snapshotAgent.enableNetConnect())\n  expectType<void>(snapshotAgent.disableNetConnect())\n}\n\n{\n  // Constructor with agent option\n  const agent = new Agent()\n  const snapshotAgent = new SnapshotAgent({\n    mode: 'record',\n    agent,\n    maxSnapshots: 25\n  })\n\n  expectAssignable<SnapshotAgent>(snapshotAgent)\n}\n\n{\n  // SnapshotRecorder standalone usage - basic options\n  const recorder = new SnapshotRecorder()\n  expectType<Promise<void>>(recorder.record({}, {}))\n  expectType<SnapshotRecorder.Snapshot | undefined>(recorder.findSnapshot({}))\n  expectType<Promise<void>>(recorder.loadSnapshots())\n  expectType<Promise<void>>(recorder.loadSnapshots('./file.json'))\n  expectType<Promise<void>>(recorder.saveSnapshots())\n  expectType<Promise<void>>(recorder.saveSnapshots('./file.json'))\n  expectType<void>(recorder.clear())\n  expectType<SnapshotRecorder.Snapshot[]>(recorder.getSnapshots())\n  expectType<number>(recorder.size())\n\n  // New methods\n  expectType<void>(recorder.resetCallCounts())\n  expectType<boolean>(recorder.deleteSnapshot({}))\n  expectType<SnapshotRecorder.SnapshotInfo | null>(recorder.getSnapshotInfo({}))\n  expectType<void>(recorder.replaceSnapshots([]))\n  expectType<void>(recorder.destroy())\n}\n\n{\n  // SnapshotRecorder with full options\n  const recorder = new SnapshotRecorder({\n    snapshotPath: './test.json',\n    mode: 'record',\n    maxSnapshots: 100,\n    autoFlush: true,\n    flushInterval: 30000,\n    matchHeaders: ['accept', 'content-type'],\n    ignoreHeaders: ['user-agent'],\n    excludeHeaders: ['authorization', 'cookie'],\n    matchBody: true,\n    matchQuery: true,\n    caseSensitive: false\n  })\n\n  expectAssignable<SnapshotRecorder>(recorder)\n}\n\n// Test Options interfaces completeness\nexpectAssignable<SnapshotRecorder.Options>({})\nexpectAssignable<SnapshotRecorder.Options>({\n  snapshotPath: './test.json'\n})\nexpectAssignable<SnapshotRecorder.Options>({\n  mode: 'playback',\n  maxSnapshots: 50\n})\nexpectAssignable<SnapshotRecorder.Options>({\n  autoFlush: true,\n  flushInterval: 15000\n})\nexpectAssignable<SnapshotRecorder.Options>({\n  matchHeaders: ['content-type'],\n  ignoreHeaders: ['authorization'],\n  excludeHeaders: ['set-cookie']\n})\nexpectAssignable<SnapshotRecorder.Options>({\n  matchBody: false,\n  matchQuery: true,\n  caseSensitive: true\n})\n\nexpectAssignable<SnapshotAgent.Options>({})\nexpectAssignable<SnapshotAgent.Options>({\n  mode: 'update',\n  snapshotPath: './snapshots.json',\n  connections: 2 // MockAgent option\n})\n\n{\n  // New snapshot structure with responses array\n  const snapshot: SnapshotRecorder.Snapshot = {\n    request: {\n      method: 'GET',\n      url: 'https://api.example.com/test',\n      headers: { authorization: 'Bearer token' },\n      body: '{\"test\": \"data\"}'\n    },\n    responses: [{\n      statusCode: 200,\n      headers: { 'content-type': 'application/json' },\n      body: 'eyJ0ZXN0IjoiZGF0YSJ9', // base64\n      trailers: {}\n    }],\n    callCount: 0,\n    timestamp: '2024-01-01T00:00:00.000Z'\n  }\n\n  expectType<string>(snapshot.request.method)\n  expectType<string>(snapshot.request.url)\n  expectType<Record<string, string>>(snapshot.request.headers)\n  expectType<string | undefined>(snapshot.request.body)\n  expectType<Array<{\n    statusCode: number\n    headers: Record<string, string>\n    body: string\n    trailers: Record<string, string>\n  }>>(snapshot.responses)\n  expectType<number>(snapshot.callCount)\n  expectType<string>(snapshot.timestamp)\n}\n\n{\n  // Sequential responses support\n  const sequentialSnapshot: SnapshotRecorder.Snapshot = {\n    request: {\n      method: 'POST',\n      url: 'https://api.example.com/sequence',\n      headers: { 'content-type': 'application/json' }\n    },\n    responses: [\n      {\n        statusCode: 201,\n        headers: { 'content-type': 'application/json' },\n        body: '{\"id\": 1}',\n        trailers: {}\n      },\n      {\n        statusCode: 200,\n        headers: { 'content-type': 'application/json' },\n        body: '{\"id\": 1, \"status\": \"updated\"}',\n        trailers: {}\n      },\n      {\n        statusCode: 204,\n        headers: {},\n        body: '',\n        trailers: {}\n      }\n    ],\n    callCount: 2,\n    timestamp: '2024-01-01T00:00:00.000Z'\n  }\n\n  expectAssignable<SnapshotRecorder.Snapshot>(sequentialSnapshot)\n}\n\n{\n  // SnapshotInfo interface test\n  const info: SnapshotRecorder.SnapshotInfo = {\n    hash: 'abc123def456',\n    request: {\n      method: 'PUT',\n      url: 'https://api.example.com/update',\n      headers: { 'content-type': 'application/json' },\n      body: '{\"data\": \"value\"}'\n    },\n    responseCount: 3,\n    callCount: 1,\n    timestamp: '2024-01-01T00:00:00.000Z'\n  }\n\n  expectType<string>(info.hash)\n  expectType<{\n    method: string\n    url: string\n    headers: Record<string, string>\n    body?: string\n  }>(info.request)\n  expectType<number>(info.responseCount)\n  expectType<number>(info.callCount)\n  expectType<string>(info.timestamp)\n}\n\n{\n  // SnapshotData interface for replacement\n  const snapshotData: SnapshotRecorder.SnapshotData = {\n    hash: 'replacement-hash',\n    snapshot: {\n      request: {\n        method: 'DELETE',\n        url: 'https://api.example.com/delete/123',\n        headers: { authorization: 'Bearer token' }\n      },\n      responses: [{\n        statusCode: 204,\n        headers: {},\n        body: '',\n        trailers: {}\n      }],\n      callCount: 0,\n      timestamp: '2024-01-01T00:00:00.000Z'\n    }\n  }\n\n  expectType<string>(snapshotData.hash)\n  expectType<SnapshotRecorder.Snapshot>(snapshotData.snapshot)\n}\n\n{\n  // Array operations for replaceSnapshots\n  const snapshotDataArray: SnapshotRecorder.SnapshotData[] = [\n    {\n      hash: 'hash1',\n      snapshot: {\n        request: { method: 'GET', url: 'https://example.com/1', headers: {} },\n        responses: [{ statusCode: 200, headers: {}, body: 'response1', trailers: {} }],\n        callCount: 0,\n        timestamp: '2024-01-01T00:00:00.000Z'\n      }\n    },\n    {\n      hash: 'hash2',\n      snapshot: {\n        request: { method: 'POST', url: 'https://example.com/2', headers: {} },\n        responses: [\n          { statusCode: 201, headers: {}, body: 'created', trailers: {} },\n          { statusCode: 200, headers: {}, body: 'updated', trailers: {} }\n        ],\n        callCount: 1,\n        timestamp: '2024-01-01T01:00:00.000Z'\n      }\n    }\n  ]\n\n  const agent = new SnapshotAgent()\n  const recorder = new SnapshotRecorder()\n\n  expectType<void>(agent.replaceSnapshots(snapshotDataArray))\n  expectType<void>(recorder.replaceSnapshots(snapshotDataArray))\n}\n\n{\n  // Test method parameter types\n  const agent = new SnapshotAgent()\n  const requestOpts = {\n    origin: 'https://api.example.com',\n    path: '/test',\n    method: 'GET',\n    headers: { accept: 'application/json' }\n  }\n\n  expectType<boolean>(agent.deleteSnapshot(requestOpts))\n  expectType<SnapshotRecorder.SnapshotInfo | null>(agent.getSnapshotInfo(requestOpts))\n\n  const recorder = new SnapshotRecorder()\n  expectType<boolean>(recorder.deleteSnapshot(requestOpts))\n  expectType<SnapshotRecorder.SnapshotInfo | null>(recorder.getSnapshotInfo(requestOpts))\n}\n\n{\n  // Test optional body in request\n  const snapshotWithoutBody: SnapshotRecorder.Snapshot = {\n    request: {\n      method: 'GET',\n      url: 'https://api.example.com/get',\n      headers: { accept: 'application/json' }\n      // body is optional\n    },\n    responses: [{\n      statusCode: 200,\n      headers: { 'content-type': 'application/json' },\n      body: 'response-data',\n      trailers: {}\n    }],\n    callCount: 0,\n    timestamp: '2024-01-01T00:00:00.000Z'\n  }\n\n  expectAssignable<SnapshotRecorder.Snapshot>(snapshotWithoutBody)\n}\n\n{\n  // Test mode type constraints\n  const recordAgent = new SnapshotAgent({ mode: 'record' })\n  const playbackAgent = new SnapshotAgent({ mode: 'playback', snapshotPath: './test.json' })\n  const updateAgent = new SnapshotAgent({ mode: 'update', snapshotPath: './test.json' })\n\n  expectType<SnapshotRecorder.SnapshotRecorderMode>(recordAgent.getMode())\n  expectType<SnapshotRecorder.SnapshotRecorderMode>(playbackAgent.getMode())\n  expectType<SnapshotRecorder.SnapshotRecorderMode>(updateAgent.getMode())\n}\n\n{\n  // Test array types for header configuration\n  const matchHeaders: string[] = ['content-type', 'accept', 'authorization']\n  const ignoreHeaders: string[] = ['user-agent', 'x-request-id']\n  const excludeHeaders: string[] = ['set-cookie', 'authorization']\n\n  expectAssignable<SnapshotAgent.Options>({\n    matchHeaders,\n    ignoreHeaders,\n    excludeHeaders\n  })\n\n  expectAssignable<SnapshotRecorder.Options>({\n    matchHeaders,\n    ignoreHeaders,\n    excludeHeaders\n  })\n}\n\n// Test boolean configuration options\nexpectAssignable<SnapshotAgent.Options>({\n  matchBody: true,\n  matchQuery: false,\n  caseSensitive: true,\n  autoFlush: false\n})\n\nexpectAssignable<SnapshotRecorder.Options>({\n  matchBody: false,\n  matchQuery: true,\n  caseSensitive: false,\n  autoFlush: true\n})\n\n// Test number configuration options\nexpectAssignable<SnapshotAgent.Options>({\n  maxSnapshots: 100,\n  flushInterval: 30000\n})\n\nexpectAssignable<SnapshotRecorder.Options>({\n  maxSnapshots: 50,\n  flushInterval: 15000\n})\n\n// Test filtering options\nexpectAssignable<SnapshotRecorder.Options>({\n  shouldRecord: (requestOpts) => requestOpts.path.startsWith('/api'),\n  shouldPlayback: (requestOpts) => !requestOpts.path.includes('admin'),\n  excludeUrls: ['/health', /\\/private\\/.*/, /token=/]\n})\n\nexpectAssignable<SnapshotAgent.Options>({\n  shouldRecord: (requestOpts) => requestOpts.method === 'GET',\n  shouldPlayback: (requestOpts) => requestOpts.path !== '/forbidden',\n  excludeUrls: ['secret', /admin/]\n})\n"
  },
  {
    "path": "test/types/util.test-d.ts",
    "content": "import { expectAssignable } from 'tsd'\nimport { util } from '../../types/util'\n\nexpectAssignable<Record<string, string | string[]>>(\n  util.parseHeaders(['content-type', 'text/plain'])\n)\n\nexpectAssignable<Record<string, string | string[]>>(\n  util.parseHeaders([Buffer.from('content-type'), Buffer.from('text/plain')])\n)\n\nexpectAssignable<Record<string, string | string[]>>(\n  util.parseHeaders(\n    [Buffer.from('content-type'), Buffer.from('text/plain')],\n    {}\n  )\n)\n\nexpectAssignable<Record<string, string | string[]>>(\n  util.parseHeaders([Buffer.from('content-type'), [Buffer.from('text/plain')]])\n)\n\nexpectAssignable<string>(util.headerNameToString('content-type'))\n\nexpectAssignable<string>(util.headerNameToString(Buffer.from('content-type')))\n"
  },
  {
    "path": "test/types/websocket.test-d.ts",
    "content": "import { ReadableStream, WritableStream } from 'node:stream/web'\nimport { expectType } from 'tsd'\nimport { WebSocketStream, ErrorEvent } from '../../types'\n\ndeclare const webSocketStream: WebSocketStream\nconst webSocketStreamOpened = await webSocketStream.opened\n\ndeclare const errorEvent: ErrorEvent\n\n// Test that the readable and writable streams are of identical types to ones from stream/web\nexpectType<WritableStream>(webSocketStreamOpened.writable)\nexpectType<ReadableStream>(webSocketStreamOpened.readable)\n\nexpectType<Error>(errorEvent.error)\n"
  },
  {
    "path": "test/upgrade-crlf.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { test, after } = require('node:test')\nconst { Client, errors } = require('..')\nconst net = require('node:net')\n\ntest('CRLF injection in upgrade header via CRLF sequence', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'websocket\\r\\n\\r\\nSET pwned true'\n      })\n      t.fail('should have thrown')\n    } catch (err) {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'invalid upgrade header')\n    }\n  })\n\n  await t.completed\n})\n\ntest('CRLF injection in upgrade header via lone CR', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'websocket\\rinjected'\n      })\n      t.fail('should have thrown')\n    } catch (err) {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'invalid upgrade header')\n    }\n  })\n\n  await t.completed\n})\n\ntest('CRLF injection in upgrade header via lone LF', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'websocket\\ninjected'\n      })\n      t.fail('should have thrown')\n    } catch (err) {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'invalid upgrade header')\n    }\n  })\n\n  await t.completed\n})\n\ntest('CRLF injection in upgrade header via null byte', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.upgrade({\n        path: '/',\n        method: 'GET',\n        protocol: 'websocket\\0injected'\n      })\n      t.fail('should have thrown')\n    } catch (err) {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'invalid upgrade header')\n    }\n  })\n\n  await t.completed\n})\n\ntest('CRLF injection in upgrade option via client.request', async (t) => {\n  t = tspl(t, { plan: 2 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    try {\n      await client.request({\n        path: '/',\n        method: 'GET',\n        upgrade: 'websocket\\r\\n\\r\\nGET /smuggled HTTP/1.1'\n      })\n      t.fail('should have thrown')\n    } catch (err) {\n      t.ok(err instanceof errors.InvalidArgumentError)\n      t.strictEqual(err.message, 'invalid upgrade header')\n    }\n  })\n\n  await t.completed\n})\n\ntest('valid upgrade value is accepted', async (t) => {\n  t = tspl(t, { plan: 1 })\n\n  const server = net.createServer({ joinDuplicateHeaders: true }, (c) => {\n    c.on('data', () => {\n      c.write('HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\n\\r\\n')\n    })\n    c.on('error', () => {})\n  })\n  after(() => server.close())\n\n  server.listen(0, async () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    const { socket } = await client.upgrade({\n      path: '/',\n      method: 'GET',\n      protocol: 'websocket'\n    })\n    t.ok(socket)\n    socket.destroy()\n  })\n\n  await t.completed\n})\n"
  },
  {
    "path": "test/util.js",
    "content": "'use strict'\n\nconst { strictEqual, throws, doesNotThrow } = require('node:assert')\nconst { test, describe } = require('node:test')\nconst { isBlobLike, parseURL, isHttpOrHttpsPrefixed, isValidPort } = require('../lib/core/util')\nconst { InvalidArgumentError } = require('../lib/core/errors')\n\ndescribe('isBlobLike', () => {\n  test('buffer', () => {\n    const buffer = Buffer.alloc(1)\n    strictEqual(isBlobLike(buffer), false)\n  })\n\n  test('blob', () => {\n    const blob = new Blob(['asd'], {\n      type: 'application/json'\n    })\n    strictEqual(isBlobLike(blob), true)\n  })\n\n  test('file', () => {\n    const file = new File(['asd'], 'file.txt', {\n      type: 'text/plain'\n    })\n    strictEqual(isBlobLike(file), true)\n  })\n\n  test('blobLikeStream', () => {\n    const blobLikeStream = {\n      [Symbol.toStringTag]: 'Blob',\n      stream: () => { }\n    }\n    strictEqual(isBlobLike(blobLikeStream), true)\n  })\n\n  test('fileLikeStream', () => {\n    const fileLikeStream = {\n      stream: () => { },\n      [Symbol.toStringTag]: 'File'\n    }\n    strictEqual(isBlobLike(fileLikeStream), true)\n  })\n\n  test('fileLikeArrayBuffer', () => {\n    const blobLikeArrayBuffer = {\n      [Symbol.toStringTag]: 'Blob',\n      arrayBuffer: () => { }\n    }\n    strictEqual(isBlobLike(blobLikeArrayBuffer), true)\n  })\n\n  test('blobLikeArrayBuffer', () => {\n    const fileLikeArrayBuffer = {\n      [Symbol.toStringTag]: 'File',\n      arrayBuffer: () => { }\n    }\n    strictEqual(isBlobLike(fileLikeArrayBuffer), true)\n  })\n\n  test('string', () => {\n    strictEqual(isBlobLike('Blob'), false)\n  })\n\n  test('null', () => {\n    strictEqual(isBlobLike(null), false)\n  })\n})\n\ndescribe('isHttpOrHttpsPrefixed', () => {\n  test('returns false for invalid values', () => {\n    strictEqual(isHttpOrHttpsPrefixed('wss:'), false)\n  })\n  test('returns true for \"http:\" or \"https:\"', () => {\n    strictEqual(isHttpOrHttpsPrefixed('http:'), true)\n    strictEqual(isHttpOrHttpsPrefixed('https:'), true)\n  })\n})\n\ndescribe('isValidPort', () => {\n  test('returns false for invalid values', () => {\n    strictEqual(isValidPort(NaN), false)\n    strictEqual(isValidPort(Infinity), false)\n    strictEqual(isValidPort(-Infinity), false)\n    strictEqual(isValidPort(NaN.toString()), false)\n    strictEqual(isValidPort(Infinity.toString()), false)\n    strictEqual(isValidPort(-Infinity.toString()), false)\n    strictEqual(isValidPort('port'), false)\n    strictEqual(isValidPort('65535i'), false)\n  })\n  test('returns true for port in range of 0 to 65535 as number', () => {\n    for (let i = 0; i < 65536; i++) {\n      strictEqual(isValidPort(i), true)\n    }\n  })\n  test('returns true for port in range of 0 to 65535 as string', () => {\n    for (let i = 0; i < 65536; i++) {\n      strictEqual(isValidPort(i.toString()), true)\n    }\n  })\n})\n\ndescribe('parseURL', () => {\n  test('throws if url is not a string or object', () => {\n    throws(() => { parseURL(null) }, new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.'))\n    throws(() => { parseURL(1) }, new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.'))\n    throws(() => { parseURL(true) }, new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.'))\n    throws(() => { parseURL(false) }, new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.'))\n    throws(() => { parseURL(false) }, new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.'))\n  })\n  test('throws if protocol is not beginning with http:', () => {\n    throws(() => { parseURL('ws://www.example.com') }, new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.'))\n  })\n  test('throws if protocol is not beginning with https:', () => {\n    throws(() => { parseURL('wss://www.example.com') }, new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.'))\n  })\n  test('returns an URL object if url is a string of an https URL', () => {\n    const url = parseURL('https://www.example.com')\n    strictEqual(url instanceof URL, true)\n    strictEqual(url.href, 'https://www.example.com/')\n  })\n  test('returns an URL object if url is a string of an http URL', () => {\n    const url = parseURL('http://www.example.com')\n    strictEqual(url instanceof URL, true)\n    strictEqual(url.href, 'http://www.example.com/')\n  })\n\n  describe('when url is an instance of URL', () => {\n    test('returns the same URL object', () => {\n      const url = new URL('https://www.example.com')\n      const parsedURL = parseURL(url)\n      strictEqual(parsedURL, url)\n    })\n    test('throws if the URL protocol is not http: or https:', () => {\n      const url = new URL('ws://www.example.com')\n      throws(() => { parseURL(url) }, new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.'))\n    })\n    test('passes if the URL protocol is http:', () => {\n      const url = new URL('http://www.example.com')\n      delete url.origin\n      doesNotThrow(() => { parseURL(url) })\n    })\n    test('passes if the URL protocol is https:', () => {\n      const url = new URL('https://www.example.com')\n      delete url.origin\n      doesNotThrow(() => { parseURL(url) })\n    })\n    test('passes if the URL protocol is http:', () => {\n      const url = new URL('http://www.example.com')\n      doesNotThrow(() => { parseURL(url) })\n    })\n    test('passes if the URL protocol is https:', () => {\n      const url = new URL('https://www.example.com')\n      doesNotThrow(() => { parseURL(url) })\n    })\n  })\n\n  describe('when url is an common object', () => {\n    test.skip('does not throw if a urlLike object is passed', () => {\n      const url = parseURL({ protocol: 'http:' })\n      console.log(url)\n    })\n\n    describe('port', () => {\n      test('throws if port is not an finite number as string', () => {\n        throws(() => parseURL({ protocol: 'http:', port: 'NaN' }), new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.'))\n      })\n      test('doesn\\'t throw if port is valid number', () => {\n        for (let i = 0; i < 65536; i++) {\n          doesNotThrow(() => parseURL({ protocol: 'http:', hostname: 'www.example.com', port: i.toString() }), i.toString())\n        }\n      })\n      test('throws if port is invalid number', () => {\n        throws(() => parseURL({ protocol: 'http:', port: '-1' }), new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.'))\n        throws(() => parseURL({ protocol: 'http:', port: '65536' }), new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.'))\n      })\n      test('sets port based on protocol', () => {\n        strictEqual(parseURL({ protocol: 'http:', hostname: 'www.example.com', path: '/' }).port, '')\n        strictEqual(parseURL({ protocol: 'https:', hostname: 'www.example.com', path: '/' }).port, '')\n      })\n      test('don\\'t override port with protocol if port was explicitly set', () => {\n        strictEqual(parseURL({ protocol: 'http:', hostname: 'www.example.com', path: '/', port: 1337 }).port, '1337')\n        strictEqual(parseURL({ protocol: 'https:', hostname: 'www.example.com', path: '/', port: 1337 }).port, '1337')\n      })\n    })\n\n    describe('path', () => {\n      test('doesn\\'t throw if path null or undefined', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', path: null })\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', path: undefined })\n      })\n      test('throws if path is not as string', () => {\n        throws(() => parseURL({ protocol: 'http:', hostname: 'www.example.com', path: 1 }), new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.'))\n      })\n      test('doesn\\'t throw if path is a string', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', path: '/' })\n      })\n      test('accepts path with and without leading /', () => {\n        strictEqual(parseURL({ protocol: 'http:', hostname: 'www.example.com', path: 'abc' }).pathname, '/abc')\n        strictEqual(parseURL({ protocol: 'https:', hostname: 'www.example.com', path: '/abc' }).pathname, '/abc')\n      })\n    })\n\n    describe('pathname', () => {\n      test('doesn\\'t throw if pathname null or undefined', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', pathname: null })\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', pathname: undefined })\n      })\n      test('throws if pathname is not as string', () => {\n        throws(() => parseURL({ protocol: 'http:', pathname: 1 }), new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.'))\n      })\n      test('doesn\\'t throw if pathname is a string', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', pathname: '/' })\n      })\n    })\n\n    describe('hostname', () => {\n      test('doesn\\'t throw if hostname null or undefined', () => {\n        parseURL({ protocol: 'http:', hostname: null, origin: 'http://www.example.com' })\n        parseURL({ protocol: 'http:', hostname: undefined, origin: 'http://www.example.com' })\n      })\n      test('throws if hostname is not as string', () => {\n        throws(() => parseURL({ protocol: 'http:', hostname: 1 }), new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.'))\n      })\n      test('doesn\\'t throw if hostname is a string', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com' })\n      })\n    })\n\n    describe('origin', () => {\n      test('doesn\\'t throw if origin null or undefined', () => {\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', origin: null })\n        parseURL({ protocol: 'http:', hostname: 'www.example.com', origin: undefined })\n      })\n      test('throws if origin is not as string', () => {\n        throws(() => parseURL({ protocol: 'http:', origin: 1 }), new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.'))\n      })\n      test('doesn\\'t throw if origin is a string', () => {\n        parseURL({ protocol: 'http:', origin: 'https://www.example.com' })\n      })\n      test('removes trailing /', () => {\n        strictEqual(parseURL({ protocol: 'http:', origin: 'https://www.example.com/' }).origin, 'https://www.example.com')\n      })\n    })\n\n    describe('protocol', () => {\n      test('throws if protocol is not http: or https: and no origin is defined', () => {\n        throws(() => parseURL({ protocol: 'wss:', hostname: 'www.example.com', path: '' }))\n      })\n      test('doesn\\'t throw when origin is not provided', () => {\n        strictEqual(parseURL({ protocol: 'http:', hostname: 'www.example.com', path: '' }).origin, 'http://www.example.com')\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/utils/async-iterators.js",
    "content": "'use strict'\n\nasync function * wrapWithAsyncIterable (asyncIterable, indefinite = false) {\n  for await (const chunk of asyncIterable) {\n    yield chunk\n  }\n  if (indefinite) {\n    await new Promise(() => {})\n  }\n}\n\nconst STREAM = 'stream'\nconst ASYNC_ITERATOR = 'async-iterator'\nfunction maybeWrapStream (stream, type) {\n  if (type === STREAM) {\n    return stream\n  }\n  if (type === ASYNC_ITERATOR) {\n    return wrapWithAsyncIterable(stream)\n  }\n\n  throw new Error(`bad input ${type} should be ${STREAM} or ${ASYNC_ITERATOR}`)\n}\n\nmodule.exports = { wrapWithAsyncIterable, maybeWrapStream, consts: { STREAM, ASYNC_ITERATOR } }\n"
  },
  {
    "path": "test/utils/date.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst { parseHttpDate } = require('../../lib/util/date')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\n\nconst skipFuzzing = runtimeFeatures.has('crypto') === false\n\ndescribe('parseHttpDate', () => {\n  test('IMF-fixdate', (t) => {\n    const values = {\n      'Sun, 06 Nov 1994 08:49:37 GMT': new Date(Date.UTC(1994, 10, 6, 8, 49, 37)),\n      'Fri, 18 Aug 1950 02:01:18 GMT': new Date(Date.UTC(1950, 7, 18, 2, 1, 18)),\n      'Wed, 11 Dec 2024 23:20:57 GMT': new Date(Date.UTC(2024, 11, 11, 23, 20, 57)),\n      'Wed,\\t11 Dec 2024 23:20:57 GMT': undefined, // Invalid whitespace\n      'Wed, 11\\tDec 2024 23:20:57 GMT': undefined, // Invalid whitespace\n      'Wed, 11 Dec\\t2024 23:20:57 GMT': undefined, // Invalid whitespace\n      'Wed, 11 Dec 2024\\t23:20:57 GMT': undefined, // Invalid whitespace\n      'Wed, 11 Dec 2024 23:20:57\\tGMT': undefined, // Invalid whitespace\n      'Wed, 11 Dec 2024 23.20:57 GMT': undefined, // Invalid separator\n      'Wed, 11 Dec 2024 23:20.57 GMT': undefined, // Invalid separator\n      'Wed, 11 Dec 2024 23:20:57 UTC': undefined, // UTC is not a valid timezone\n      'Wed, aa Dec 2024 23:20:57 GMT': undefined, // NaN day\n      'aaa, 06 Dec 2024 23:20:57 GMT': undefined, // Invalid day name\n      'Wed, 01 aaa 2024 23:20:57 GMT': undefined, // Invalid month\n      'Wed, 6 Dec 2024 23:20:07 GMT': undefined, // No leading zero\n      'Wed, 06 Dec 2024 3:20:07 GMT': undefined, // No leading zero\n      'Wed, 06 Dec 2024 23:1:07 GMT': undefined, // No leading zero\n      'Wed, 06 Dec 2024 23:01:7 GMT': undefined, // No leading zero\n      'Wed, a6 Dec 2024 23:01:07 GMT': undefined, // No leading zero\n      'Wed, 06 Dec aaaa 23:01:07 GMT': undefined, // NaN year\n      'Wed, 06 Dec 2024 aa:01:07 GMT': undefined, // NaN hour\n      'Wed, 06 Dec 2024 23:aa:07 GMT': undefined, // NaN min\n      'Wed, 06 Dec 2024 23:01:a7 GMT': undefined, // NaN sec\n      'Wed, 06 Dec 2024 23:01:+7 GMT': undefined, // NaN sec\n      'Wed, 06 Dec 2024 23:01:aa GMT': undefined // NaN sec\n    }\n\n    for (const date of Object.keys(values)) {\n      t.assert.deepStrictEqual(parseHttpDate(date), values[date], date)\n    }\n  })\n\n  test('RFC850', (t) => {\n    const values = {\n      'Sunday, 06-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 6, 8, 49, 37)),\n      'Monday, 07-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 7, 8, 49, 37)),\n      'Tuesday, 08-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 8, 8, 49, 37)),\n      'Wednesday, 09-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 9, 8, 49, 37)),\n      'Thursday, 10-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 10, 8, 49, 37)),\n      'Friday, 11-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 11, 8, 49, 37)),\n      'Saturday, 12-Nov-94 08:49:37 GMT': new Date(Date.UTC(1994, 10, 12, 8, 49, 37)),\n\n      'Thursday, 18-Aug-50 02:01:18 GMT': new Date(Date.UTC(2050, 7, 18, 2, 1, 18)),\n      'Wednesday, 11-Dec-24 23:20:57 GMT': new Date(Date.UTC(2024, 11, 11, 23, 20, 57)),\n      'Wednesday, 11-Dec-24 23:20:57 UTC': undefined, // UTC is not a valid timezone\n      'Wednesday  11-Dec-24 23:20:57 GMT': undefined, // no comma\n      'Wednesday, 11-Dec-bc 23:20:57 GMT': undefined, // NaN year\n      'Wednesday, aa Dec 2024 23:20:57 GMT': undefined, // NaN day\n      'Thursday, +7-Aug-50 02:01:18 GMT': undefined, // NaN day\n      'Funday, 06-Nov-94 08:49:37 GMT': undefined, // invalid day name\n      'aaa, 06 Dec 2024 23:20:57 GMT': undefined, // Invalid day name\n      'Wednesday, 01-aaa-24 23:20:57 GMT': undefined, // Invalid month\n      'Wednesday, 6-Dec-24 23:20:07 GMT': undefined, // No leading zero\n      'Wednesday, 06-Dec-24 3:20:07 GMT': undefined, // No leading zero\n      'Wednesday, 06-Dec-24 23:1:07 GMT': undefined, // No leading zero\n      'Wednesday, 06-Dec-24 23:01:7 GMT': undefined, // No leading zero\n      'Wednesday, 06 Dec-aa 23:01:07 GMT': undefined, // NaN year\n      'Wednesday, 06-Dec-24 aa:01:07 GMT': undefined, // NaN hour\n      'Wednesday, 06-Dec-24 23:aa:07 GMT': undefined, // NaN min\n      'Wednesday, 06-Dec-24 23:01:+7 GMT': undefined, // NaN min\n      'Wednesday, 06-Dec-24 23:01:aa GMT': undefined // NaN sec\n    }\n\n    for (const date of Object.keys(values)) {\n      t.assert.deepStrictEqual(parseHttpDate(date), values[date], date)\n    }\n  })\n\n  test('asctime()', (t) => {\n    const values = {\n      'Sun Nov  6 08:49:37 1994': new Date(Date.UTC(1994, 10, 6, 8, 49, 37)),\n      'Fri Aug 18 02:01:18 1950': new Date(Date.UTC(1950, 7, 18, 2, 1, 18)),\n      'Wed Dec 11 23:20:57 2024': new Date(Date.UTC(2024, 11, 11, 23, 20, 57)),\n\n      'Mon Jan  1 23:20:57 2024': new Date(Date.UTC(2024, 0, 1, 23, 20, 57)),\n      'Thu Feb  1 23:20:57 2024': new Date(Date.UTC(2024, 1, 1, 23, 20, 57)),\n      'Fri Mar  1 23:20:57 2024': new Date(Date.UTC(2024, 2, 1, 23, 20, 57)),\n      'Mon Apr  1 23:20:57 2024': new Date(Date.UTC(2024, 3, 1, 23, 20, 57)),\n      'Wed May  1 23:20:57 2024': new Date(Date.UTC(2024, 4, 1, 23, 20, 57)),\n      'Sat Jun  1 23:20:57 2024': new Date(Date.UTC(2024, 5, 1, 23, 20, 57)),\n      'Mon Jul  1 23:20:57 2024': new Date(Date.UTC(2024, 6, 1, 23, 20, 57)),\n      'Thu Aug  1 23:20:57 2024': new Date(Date.UTC(2024, 7, 1, 23, 20, 57)),\n      'Sun Sep  1 23:20:57 2024': new Date(Date.UTC(2024, 8, 1, 23, 20, 57)),\n      'Tue Oct  1 23:20:57 2024': new Date(Date.UTC(2024, 9, 1, 23, 20, 57)),\n      'Fri Nov  1 23:20:57 2024': new Date(Date.UTC(2024, 10, 1, 23, 20, 57)),\n      'Sun Dec  1 23:20:57 2024': new Date(Date.UTC(2024, 11, 1, 23, 20, 57)),\n\n      'Mon Jan  1 00:20:57 2024': new Date(Date.UTC(2024, 0, 1, 0, 20, 57)),\n      'Mon Jan  1 01:20:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 20, 57)),\n      'Mon Jan  1 02:20:57 2024': new Date(Date.UTC(2024, 0, 1, 2, 20, 57)),\n      'Mon Jan  1 03:20:57 2024': new Date(Date.UTC(2024, 0, 1, 3, 20, 57)),\n      'Mon Jan  1 04:20:57 2024': new Date(Date.UTC(2024, 0, 1, 4, 20, 57)),\n      'Mon Jan  1 05:20:57 2024': new Date(Date.UTC(2024, 0, 1, 5, 20, 57)),\n      'Mon Jan  1 06:20:57 2024': new Date(Date.UTC(2024, 0, 1, 6, 20, 57)),\n      'Mon Jan  1 07:20:57 2024': new Date(Date.UTC(2024, 0, 1, 7, 20, 57)),\n      'Mon Jan  1 08:20:57 2024': new Date(Date.UTC(2024, 0, 1, 8, 20, 57)),\n      'Mon Jan  1 09:20:57 2024': new Date(Date.UTC(2024, 0, 1, 9, 20, 57)),\n      'Mon Jan  1 10:20:57 2024': new Date(Date.UTC(2024, 0, 1, 10, 20, 57)),\n      'Mon Jan  1 11:20:57 2024': new Date(Date.UTC(2024, 0, 1, 11, 20, 57)),\n      'Mon Jan  1 12:20:57 2024': new Date(Date.UTC(2024, 0, 1, 12, 20, 57)),\n      'Mon Jan  1 13:20:57 2024': new Date(Date.UTC(2024, 0, 1, 13, 20, 57)),\n      'Mon Jan  1 14:20:57 2024': new Date(Date.UTC(2024, 0, 1, 14, 20, 57)),\n      'Mon Jan  1 15:20:57 2024': new Date(Date.UTC(2024, 0, 1, 15, 20, 57)),\n      'Mon Jan  1 16:20:57 2024': new Date(Date.UTC(2024, 0, 1, 16, 20, 57)),\n      'Mon Jan  1 17:20:57 2024': new Date(Date.UTC(2024, 0, 1, 17, 20, 57)),\n      'Mon Jan  1 18:20:57 2024': new Date(Date.UTC(2024, 0, 1, 18, 20, 57)),\n      'Mon Jan  1 19:20:57 2024': new Date(Date.UTC(2024, 0, 1, 19, 20, 57)),\n      'Mon Jan  1 20:20:57 2024': new Date(Date.UTC(2024, 0, 1, 20, 20, 57)),\n      'Mon Jan  1 21:20:57 2024': new Date(Date.UTC(2024, 0, 1, 21, 20, 57)),\n      'Mon Jan  1 22:20:57 2024': new Date(Date.UTC(2024, 0, 1, 22, 20, 57)),\n      // 'Mon Jan  1 23:20:57 2024': new Date(Date.UTC(2024, 0, 1, 23, 20, 57)),\n      'Mon Jan  1 24:20:57 2024': undefined, // Invalid hour\n\n      'Mon Jan  1 01:00:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 0, 57)),\n      'Mon Jan  1 01:01:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 1, 57)),\n      'Mon Jan  1 01:02:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 2, 57)),\n      'Mon Jan  1 01:03:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 3, 57)),\n      'Mon Jan  1 01:04:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 4, 57)),\n      'Mon Jan  1 01:05:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 5, 57)),\n      'Mon Jan  1 01:06:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 6, 57)),\n      'Mon Jan  1 01:07:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 7, 57)),\n      'Mon Jan  1 01:08:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 8, 57)),\n      'Mon Jan  1 01:09:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 9, 57)),\n      'Mon Jan  1 01:10:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 10, 57)),\n      'Mon Jan  1 01:11:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 11, 57)),\n      'Mon Jan  1 01:12:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 12, 57)),\n      'Mon Jan  1 01:13:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 13, 57)),\n      'Mon Jan  1 01:14:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 14, 57)),\n      'Mon Jan  1 01:15:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 15, 57)),\n      'Mon Jan  1 01:16:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 16, 57)),\n      'Mon Jan  1 01:17:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 17, 57)),\n      'Mon Jan  1 01:18:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 18, 57)),\n      'Mon Jan  1 01:19:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 19, 57)),\n      // 'Mon Jan  1 01:20:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 20, 57)),\n      'Mon Jan  1 01:21:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 21, 57)),\n      'Mon Jan  1 01:22:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 22, 57)),\n      'Mon Jan  1 01:23:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 23, 57)),\n      'Mon Jan  1 01:24:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 24, 57)),\n      'Mon Jan  1 01:25:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 25, 57)),\n      'Mon Jan  1 01:26:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 26, 57)),\n      'Mon Jan  1 01:27:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 27, 57)),\n      'Mon Jan  1 01:28:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 28, 57)),\n      'Mon Jan  1 01:29:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 29, 57)),\n      'Mon Jan  1 01:30:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 30, 57)),\n      'Mon Jan  1 01:31:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 31, 57)),\n      'Mon Jan  1 01:32:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 32, 57)),\n      'Mon Jan  1 01:33:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 33, 57)),\n      'Mon Jan  1 01:34:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 34, 57)),\n      'Mon Jan  1 01:35:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 35, 57)),\n      'Mon Jan  1 01:36:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 36, 57)),\n      'Mon Jan  1 01:37:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 37, 57)),\n      'Mon Jan  1 01:38:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 38, 57)),\n      'Mon Jan  1 01:39:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 39, 57)),\n      'Mon Jan  1 01:40:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 40, 57)),\n      'Mon Jan  1 01:41:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 41, 57)),\n      'Mon Jan  1 01:42:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 42, 57)),\n      'Mon Jan  1 01:43:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 43, 57)),\n      'Mon Jan  1 01:44:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 44, 57)),\n      'Mon Jan  1 01:45:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 45, 57)),\n      'Mon Jan  1 01:46:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 46, 57)),\n      'Mon Jan  1 01:47:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 47, 57)),\n      'Mon Jan  1 01:48:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 48, 57)),\n      'Mon Jan  1 01:49:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 49, 57)),\n      'Mon Jan  1 01:50:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 50, 57)),\n      'Mon Jan  1 01:51:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 51, 57)),\n      'Mon Jan  1 01:52:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 52, 57)),\n      'Mon Jan  1 01:53:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 53, 57)),\n      'Mon Jan  1 01:54:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 54, 57)),\n      'Mon Jan  1 01:55:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 55, 57)),\n      'Mon Jan  1 01:56:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 56, 57)),\n      'Mon Jan  1 01:57:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 57, 57)),\n      'Mon Jan  1 01:58:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 58, 57)),\n      'Mon Jan  1 01:59:57 2024': new Date(Date.UTC(2024, 0, 1, 1, 59, 57)),\n      'Mon Jan  1 01:60:57 2024': undefined, // Invalid minute\n\n      'Mon Jan  1 02:00:00 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 0)),\n      'Mon Jan  1 02:00:01 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 1)),\n      'Mon Jan  1 02:00:02 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 2)),\n      'Mon Jan  1 02:00:03 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 3)),\n      'Mon Jan  1 02:00:04 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 4)),\n      'Mon Jan  1 02:00:05 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 5)),\n      'Mon Jan  1 02:00:06 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 6)),\n      'Mon Jan  1 02:00:07 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 7)),\n      'Mon Jan  1 02:00:08 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 8)),\n      'Mon Jan  1 02:00:09 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 9)),\n      'Mon Jan  1 02:00:10 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 10)),\n      'Mon Jan  1 02:00:11 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 11)),\n      'Mon Jan  1 02:00:12 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 12)),\n      'Mon Jan  1 02:00:13 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 13)),\n      'Mon Jan  1 02:00:14 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 14)),\n      'Mon Jan  1 02:00:15 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 15)),\n      'Mon Jan  1 02:00:16 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 16)),\n      'Mon Jan  1 02:00:17 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 17)),\n      'Mon Jan  1 02:00:18 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 18)),\n      'Mon Jan  1 02:00:19 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 19)),\n      'Mon Jan  1 02:00:20 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 20)),\n      'Mon Jan  1 02:00:21 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 21)),\n      'Mon Jan  1 02:00:22 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 22)),\n      'Mon Jan  1 02:00:23 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 23)),\n      'Mon Jan  1 02:00:24 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 24)),\n      'Mon Jan  1 02:00:25 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 25)),\n      'Mon Jan  1 02:00:26 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 26)),\n      'Mon Jan  1 02:00:27 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 27)),\n      'Mon Jan  1 02:00:28 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 28)),\n      'Mon Jan  1 02:00:29 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 29)),\n      'Mon Jan  1 02:00:30 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 30)),\n      'Mon Jan  1 02:00:31 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 31)),\n      'Mon Jan  1 02:00:32 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 32)),\n      'Mon Jan  1 02:00:33 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 33)),\n      'Mon Jan  1 02:00:34 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 34)),\n      'Mon Jan  1 02:00:35 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 35)),\n      'Mon Jan  1 02:00:36 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 36)),\n      'Mon Jan  1 02:00:37 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 37)),\n      'Mon Jan  1 02:00:38 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 38)),\n      'Mon Jan  1 02:00:39 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 39)),\n      'Mon Jan  1 02:00:40 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 40)),\n      'Mon Jan  1 02:00:41 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 41)),\n      'Mon Jan  1 02:00:42 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 42)),\n      'Mon Jan  1 02:00:43 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 43)),\n      'Mon Jan  1 02:00:44 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 44)),\n      'Mon Jan  1 02:00:45 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 45)),\n      'Mon Jan  1 02:00:46 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 46)),\n      'Mon Jan  1 02:00:47 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 47)),\n      'Mon Jan  1 02:00:48 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 48)),\n      'Mon Jan  1 02:00:49 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 49)),\n      'Mon Jan  1 02:00:50 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 50)),\n      'Mon Jan  1 02:00:51 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 51)),\n      'Mon Jan  1 02:00:52 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 52)),\n      'Mon Jan  1 02:00:53 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 53)),\n      'Mon Jan  1 02:00:54 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 54)),\n      'Mon Jan  1 02:00:55 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 55)),\n      'Mon Jan  1 02:00:56 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 56)),\n      'Mon Jan  1 02:00:57 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 57)),\n      'Mon Jan  1 02:00:58 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 58)),\n      'Mon Jan  1 02:00:59 2024': new Date(Date.UTC(2024, 0, 1, 2, 0, 59)),\n      'Mon Jan  1 02:00:60 2024': undefined, // Invalid second\n\n      'Wed Dec aa 23:20:57 2024': undefined, // NaN day\n      'aaa Dec 06 23:20:57 2024': undefined, // Invalid day name\n      'Wed aaa 01 23:20:57 2024': undefined, // Invalid month\n      'Wed Dec 11 +3:20:57 2024': undefined, // No leading zero\n      'Wed Dec 11 23:+0:57 2024': undefined, // No leading zero\n      'Wed Dec 11 23:20:+7 2024': undefined, // No leading zero\n      'Wed Dec 11 23:20:07 t024': undefined, // No leading zero\n      'Wed Dec 6 23:20:07 2024': undefined, // No leading zero\n      'Wed Dec 06 3:20:07 2024': undefined, // No leading zero\n      'Wed Dec 06 23:1:07 2024': undefined, // No leading zero\n      'Wed Dec 06 23:01:7 2024': undefined, // No leading zero\n      'Wed 06 Dec 23:01:07 aaaa': undefined, // NaN year\n      'Wed Dec 06 aa:01:07 2024': undefined, // NaN hour\n      'Wed Dec 06 23:aa:07 2024': undefined, // NaN min\n      'Wed Dec 06 23:01:aa 2024': undefined // NaN sec\n    }\n\n    for (const date of Object.keys(values)) {\n      t.assert.deepStrictEqual(parseHttpDate(date), values[date], date)\n    }\n  })\n\n  const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n  const daysLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']\n  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n  const fuzzingCount = 1e6\n\n  test('fuzzing asctime', { skip: skipFuzzing }, (t) => {\n    const { randomInt } = require('node:crypto')\n\n    function asctime (date) {\n      let result = ''\n\n      result += days[date.getUTCDay()]\n      result += ' '\n      result += months[date.getUTCMonth()]\n      result += ' '\n      result += date.getUTCDate().toString().padStart(2, ' ')\n      result += ' '\n      result += date.getUTCHours().toString().padStart(2, '0')\n      result += ':'\n      result += date.getUTCMinutes().toString().padStart(2, '0')\n      result += ':'\n      result += date.getUTCSeconds().toString().padStart(2, '0')\n      result += ' '\n      result += date.getUTCFullYear().toString().padStart(4, '0')\n\n      return result\n    }\n\n    for (let i = 0; i < fuzzingCount; i++) {\n      const date = new Date(Date.UTC(\n        randomInt(0, 9999), // Year between 0 and 3000\n        randomInt(0, 11),   // Month between 0 and 11\n        randomInt(1, 31),   // Day between 1 and 31\n        randomInt(0, 23),   // Hour between 0 and 23\n        randomInt(0, 59),   // Minute between 0 and 59\n        randomInt(0, 59)    // Second between 0 and 59\n      ))\n\n      const dateAsAscTime = asctime(date)\n      t.assert.deepStrictEqual(parseHttpDate(dateAsAscTime), date, `Fuzzing failed for: ${dateAsAscTime}`)\n    }\n  })\n\n  test('fuzzing imf', { skip: skipFuzzing }, (t) => {\n    const { randomInt } = require('node:crypto')\n\n    for (let i = 0; i < fuzzingCount; i++) {\n      const date = new Date(Date.UTC(\n        randomInt(0, 9999), // Year between 0 and 3000\n        randomInt(0, 11),   // Month between 0 and 11\n        randomInt(1, 31),   // Day between 1 and 31\n        randomInt(0, 23),   // Hour between 0 and 23\n        randomInt(0, 59),   // Minute between 0 and 59\n        randomInt(0, 59)    // Second between 0 and 59\n      ))\n\n      const dateAsImf = date.toUTCString()\n      t.assert.deepStrictEqual(parseHttpDate(dateAsImf), date, `Fuzzing failed for: ${dateAsImf}`)\n    }\n  })\n\n  test('fuzzing rfc850', { skip: skipFuzzing }, (t) => {\n    const { randomInt } = require('node:crypto')\n\n    function rfc850 (date) {\n      let result = ''\n\n      result += daysLong[date.getUTCDay()]\n      result += ', '\n      result += date.getUTCDate().toString().padStart(2, '0')\n      result += '-'\n      result += months[date.getUTCMonth()]\n      result += '-'\n      result += date.getUTCFullYear().toString().slice(2)\n      result += ' '\n      result += date.getUTCHours().toString().padStart(2, '0')\n      result += ':'\n      result += date.getUTCMinutes().toString().padStart(2, '0')\n      result += ':'\n      result += date.getUTCSeconds().toString().padStart(2, '0')\n      result += ' GMT'\n\n      return result\n    }\n\n    const minYear = 1970\n    const maxYear = 2069\n\n    for (let i = 0; i < fuzzingCount; i++) {\n      const date = new Date(Date.UTC(\n        randomInt(minYear, maxYear), // Year between minYear and maxYear\n        randomInt(0, 11),   // Month between 0 and 11\n        randomInt(1, 31),   // Day between 1 and 31\n        randomInt(0, 23),   // Hour between 0 and 23\n        randomInt(0, 59),   // Minute between 0 and 59\n        randomInt(0, 59)    // Second between 0 and 59\n      ))\n\n      const dateAsRfc850 = rfc850(date)\n      t.assert.deepStrictEqual(parseHttpDate(dateAsRfc850), date, `Fuzzing failed for: ${dateAsRfc850}`)\n    }\n  })\n})\n"
  },
  {
    "path": "test/utils/esm-wrapper.mjs",
    "content": "import { createServer } from 'node:http'\nimport { test, after } from 'node:test'\nimport {\n  Agent,\n  Client,\n  errors,\n  pipeline,\n  Pool,\n  request,\n  connect,\n  upgrade,\n  setGlobalDispatcher,\n  getGlobalDispatcher,\n  stream\n} from '../../index.js'\n\ntest('imported Client works with basic GET', (t, done) => {\n  t.plan(10)\n\n  const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n    t.assert.strictEqual('/', req.url)\n    t.assert.strictEqual('GET', req.method)\n    t.assert.strictEqual(`localhost:${server.address().port}`, req.headers.host)\n    t.assert.strictEqual(undefined, req.headers.foo)\n    t.assert.strictEqual('bar', req.headers.bar)\n    t.assert.strictEqual(undefined, req.headers['content-length'])\n    res.setHeader('Content-Type', 'text/plain')\n    res.end('hello')\n  })\n\n  after(() => server.close())\n\n  const reqHeaders = {\n    foo: undefined,\n    bar: 'bar'\n  }\n\n  server.listen(0, () => {\n    const client = new Client(`http://localhost:${server.address().port}`)\n    after(() => client.close())\n\n    client.request({\n      path: '/',\n      method: 'GET',\n      headers: reqHeaders\n    }, (err, data) => {\n      t.assert.ifError(err)\n      const { statusCode, headers, body } = data\n      t.assert.strictEqual(statusCode, 200)\n      t.assert.strictEqual(headers['content-type'], 'text/plain')\n      const bufs = []\n      body.on('data', (buf) => {\n        bufs.push(buf)\n      })\n      body.on('end', () => {\n        t.assert.strictEqual('hello', Buffer.concat(bufs).toString('utf8'))\n        done()\n      })\n    })\n  })\n})\n\ntest('imported errors work with request args validation', (t) => {\n  t.plan(2)\n\n  const client = new Client('http://localhost:5000')\n\n  client.request(null, (err) => {\n    t.assert.ok(err instanceof errors.InvalidArgumentError)\n  })\n\n  try {\n    client.request(null, 'asd')\n  } catch (err) {\n    t.assert.ok(err instanceof errors.InvalidArgumentError)\n  }\n})\n\ntest('imported errors work with request args validation promise', (t) => {\n  t.plan(1)\n\n  const client = new Client('http://localhost:5000')\n\n  client.request(null).catch((err) => {\n    t.assert.ok(err instanceof errors.InvalidArgumentError)\n  })\n})\n\ntest('named exports', (t) => {\n  t.plan(10)\n  t.assert.strictEqual(typeof Client, 'function')\n  t.assert.strictEqual(typeof Pool, 'function')\n  t.assert.strictEqual(typeof Agent, 'function')\n  t.assert.strictEqual(typeof request, 'function')\n  t.assert.strictEqual(typeof stream, 'function')\n  t.assert.strictEqual(typeof pipeline, 'function')\n  t.assert.strictEqual(typeof connect, 'function')\n  t.assert.strictEqual(typeof upgrade, 'function')\n  t.assert.strictEqual(typeof setGlobalDispatcher, 'function')\n  t.assert.strictEqual(typeof getGlobalDispatcher, 'function')\n})\n"
  },
  {
    "path": "test/utils/event-loop-blocker.js",
    "content": "'use strict'\n\nfunction eventLoopBlocker (ms) {\n  const nil = new Int32Array(new SharedArrayBuffer(4))\n  Atomics.wait(nil, 0, 0, ms)\n}\n\nmodule.exports = {\n  eventLoopBlocker\n}\n"
  },
  {
    "path": "test/utils/formdata.js",
    "content": "'use strict'\n\nconst Busboy = require('@fastify/busboy')\n\nfunction parseFormDataString (\n  body,\n  contentType\n) {\n  const cache = {\n    fileMap: new Map(),\n    fields: []\n  }\n\n  const bb = new Busboy({\n    headers: {\n      'content-type': contentType\n    }\n  })\n\n  return new Promise((resolve, reject) => {\n    bb.on('file', (name, file, filename, encoding, mimeType) => {\n      cache.fileMap.set(name, { data: [], info: { filename, encoding, mimeType } })\n\n      file.on('data', (data) => {\n        const old = cache.fileMap.get(name)\n\n        cache.fileMap.set(name, {\n          data: [...old.data, data],\n          info: old.info\n        })\n      }).on('end', () => {\n        const old = cache.fileMap.get(name)\n\n        cache.fileMap.set(name, {\n          data: Buffer.concat(old.data),\n          info: old.info\n        })\n      })\n    })\n\n    bb.on('field', (key, value) => cache.fields.push({ key, value }))\n    bb.on('finish', () => resolve(cache))\n    bb.on('error', (e) => reject(e))\n\n    bb.end(body)\n  })\n}\n\nmodule.exports = {\n  parseFormDataString\n}\n"
  },
  {
    "path": "test/utils/hello-world-server.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst hostname = '127.0.0.1'\n\nconst server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {\n  res.statusCode = 200\n  res.setHeader('Content-Type', 'text/plain')\n\n  await sendInDelayedChunks(res, 'Hello World', 125)\n  res.end()\n})\n\nasync function sendInDelayedChunks (res, payload, delay) {\n  const chunks = payload.split('')\n\n  for (const chunk of chunks) {\n    await new Promise(resolve => setTimeout(resolve, delay))\n\n    res.write(chunk)\n  }\n}\n\nserver.listen(0, hostname, () => {\n  if (process.send) {\n    process.send(`http://${hostname}:${server.address().port}/`)\n  } else {\n    console.log(`http://${hostname}:${server.address().port}/`)\n  }\n})\n"
  },
  {
    "path": "test/utils/node-http.js",
    "content": "'use strict'\n\nconst util = require('node:util')\n\nfunction closeServerAsPromise (server) {\n  return () => {\n    server.closeIdleConnections()\n    return util.promisify(server.close.bind(server))()\n  }\n}\n\nfunction closeClientAndServerAsPromise (client, server) {\n  const closeClient = util.promisify(client.close.bind(client))\n  const closeServer = util.promisify(server.close.bind(server))\n  return async () => {\n    await closeClient()\n    await closeServer()\n  }\n}\n\nmodule.exports = {\n  closeServerAsPromise,\n  closeClientAndServerAsPromise\n}\n"
  },
  {
    "path": "test/utils/redirecting-servers.js",
    "content": "'use strict'\n\nconst { createServer } = require('node:http')\nconst { after } = require('node:test')\n\nfunction close (server) {\n  return function () {\n    return new Promise(resolve => {\n      server.closeAllConnections()\n      server.close(resolve)\n    })\n  }\n}\n\nfunction startServer (handler) {\n  return new Promise(resolve => {\n    const server = createServer({ joinDuplicateHeaders: true }, handler)\n\n    server.listen(0, () => {\n      resolve(`localhost:${server.address().port}`)\n    })\n\n    after(close(server))\n  })\n}\n\nasync function startRedirectingServer () {\n  const server = await startServer((req, res) => {\n    // Parse the path and normalize arguments\n    let [code, redirections, query] = req.url\n      .slice(1)\n      .split(/[/?]/)\n\n    if (req.url.indexOf('?') !== -1 && !query) {\n      query = redirections\n      redirections = 0\n    }\n\n    code = parseInt(code, 10)\n    redirections = parseInt(redirections, 10)\n\n    if (isNaN(code) || code < 0) {\n      code = 302\n    } else if (code < 300) {\n      res.statusCode = code\n      redirections = 5\n    }\n\n    if (isNaN(redirections) || redirections < 0) {\n      redirections = 0\n    }\n\n    // On 303, the method must be GET or HEAD after the first redirect\n    if (code === 303 && redirections > 0 && req.method !== 'GET' && req.method !== 'HEAD') {\n      res.statusCode = 400\n      res.setHeader('Connection', 'close')\n      res.end('Did not switch to GET')\n      return\n    }\n\n    // End the chain at some point\n    if (redirections === 5) {\n      res.setHeader('Connection', 'close')\n      res.write(\n        `${req.method} /${redirections}${query ? ` ${query}` : ''} :: ${Object.entries(req.headers)\n          .map(([k, v]) => `${k}@${v}`)\n          .join(' ')}`\n      )\n\n      if (parseInt(req.headers['content-length']) > 0) {\n        res.write(' :: ')\n        req.pipe(res)\n      } else {\n        res.end('')\n      }\n\n      return\n    }\n\n    // Redirect by default\n    res.statusCode = code\n    res.setHeader('Connection', 'close')\n    res.setHeader('Location', `http://${server}/${code}/${++redirections}${query ? `?${query}` : ''}`)\n    res.end('')\n  })\n\n  return server\n}\n\nasync function startRedirectingWithBodyServer () {\n  const server = await startServer((req, res) => {\n    if (req.url === '/') {\n      res.statusCode = 301\n      res.setHeader('Connection', 'close')\n      res.setHeader('Location', `http://${server}/end`)\n      res.end('REDIRECT')\n      return\n    }\n\n    res.setHeader('Connection', 'close')\n    res.end('FINAL')\n  })\n\n  return server\n}\n\nfunction startRedirectingWithoutLocationServer () {\n  return startServer((req, res) => {\n    // Parse the path and normalize arguments\n    let [code] = req.url\n      .slice(1)\n      .split('/')\n      .map(r => parseInt(r, 10))\n\n    if (isNaN(code) || code < 0) {\n      code = 302\n    }\n\n    res.statusCode = code\n    res.setHeader('Connection', 'close')\n    res.end('')\n  })\n}\n\nasync function startRedirectingChainServers () {\n  const server1 = await startServer((req, res) => {\n    if (req.url === '/') {\n      res.statusCode = 301\n      res.setHeader('Connection', 'close')\n      res.setHeader('Location', `http://${server2}/`)\n      res.end('')\n      return\n    }\n\n    res.setHeader('Connection', 'close')\n    res.end(req.method)\n  })\n\n  const server2 = await startServer((req, res) => {\n    res.statusCode = 301\n    res.setHeader('Connection', 'close')\n\n    if (req.url === '/') {\n      res.setHeader('Location', `http://${server3}/`)\n    } else {\n      res.setHeader('Location', `http://${server3}/end`)\n    }\n\n    res.end('')\n  })\n\n  const server3 = await startServer((req, res) => {\n    res.statusCode = 301\n    res.setHeader('Connection', 'close')\n\n    if (req.url === '/') {\n      res.setHeader('Location', `http://${server2}/end`)\n    } else {\n      res.setHeader('Location', `http://${server1}/end`)\n    }\n\n    res.end('')\n  })\n\n  return [server1, server2, server3]\n}\n\nasync function startRedirectingWithAuthorization (authorization) {\n  const server1 = await startServer((req, res) => {\n    if (req.headers.authorization !== authorization) {\n      res.statusCode = 403\n      res.setHeader('Connection', 'close')\n      res.end('')\n      return\n    }\n\n    res.statusCode = 301\n    res.setHeader('Connection', 'close')\n\n    res.setHeader('Location', `http://${server2}`)\n    res.end('')\n  })\n\n  const server2 = await startServer((req, res) => {\n    res.end(req.headers.authorization || '')\n  })\n\n  return [server1, server2]\n}\n\nasync function startRedirectingWithCookie (cookie) {\n  const server1 = await startServer((req, res) => {\n    if (req.headers.cookie !== cookie) {\n      res.statusCode = 403\n      res.setHeader('Connection', 'close')\n      res.end('')\n      return\n    }\n\n    res.statusCode = 301\n    res.setHeader('Connection', 'close')\n\n    res.setHeader('Location', `http://${server2}`)\n    res.end('')\n  })\n\n  const server2 = await startServer((req, res) => {\n    res.end(req.headers.cookie || '')\n  })\n\n  return [server1, server2]\n}\n\nasync function startRedirectingWithRelativePath (t) {\n  const server = await startServer((req, res) => {\n    res.setHeader('Connection', 'close')\n\n    if (req.url === '/') {\n      res.statusCode = 301\n      res.setHeader('Location', '/absolute/a')\n      res.end('')\n    } else if (req.url === '/absolute/a') {\n      res.statusCode = 301\n      res.setHeader('Location', 'b')\n      res.end('')\n    } else {\n      res.statusCode = 200\n      res.end(req.url)\n    }\n  })\n\n  return server\n}\n\nasync function startRedirectingWithQueryParams (t) {\n  const server = await startServer((req, res) => {\n    if (req.url === '/?param1=first') {\n      res.statusCode = 301\n      res.setHeader('Connection', 'close')\n      res.setHeader('Location', `http://${server}/?param2=second`)\n      res.end('REDIRECT')\n      return\n    }\n\n    res.setHeader('Connection', 'close')\n    res.end('')\n  })\n\n  return server\n}\n\nmodule.exports = {\n  startServer,\n  startRedirectingServer,\n  startRedirectingWithBodyServer,\n  startRedirectingWithoutLocationServer,\n  startRedirectingChainServers,\n  startRedirectingWithAuthorization,\n  startRedirectingWithCookie,\n  startRedirectingWithRelativePath,\n  startRedirectingWithQueryParams\n}\n"
  },
  {
    "path": "test/utils/stream.js",
    "content": "'use strict'\n\nconst { Readable, Writable } = require('node:stream')\n\nfunction createReadable (data) {\n  return new Readable({\n    read () {\n      this.push(Buffer.from(data))\n      this.push(null)\n    }\n  })\n}\n\nfunction createWritable (target) {\n  return new Writable({\n    write (chunk, _, callback) {\n      target.push(chunk.toString())\n      callback()\n    },\n    final (callback) {\n      callback()\n    }\n  })\n}\n\nclass Source {\n  constructor (data) {\n    this.data = data\n  }\n\n  async start (controller) {\n    this.controller = controller\n  }\n\n  async pull (controller) {\n    controller.enqueue(this.data)\n    controller.close()\n  }\n}\n\nfunction createReadableStream (data) {\n  return new ReadableStream(new Source(data))\n}\n\nmodule.exports = { createReadableStream, createReadable, createWritable }\n"
  },
  {
    "path": "test/web-platform-tests/expectation.json",
    "content": "{\n  \"fetch\": {\n    \"api\": {\n      \"basic\": {\n        \"request-head.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch with HEAD with body\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-null-body.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Response.body is null for responses with status=204 (method=GET)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=204 (method=POST)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=204 (method=OPTIONS)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=205 (method=GET)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=205 (method=POST)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=205 (method=OPTIONS)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=304 (method=GET)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=304 (method=POST)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with status=304 (method=OPTIONS)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.body is null for responses with method=HEAD\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Null body status with subresource integrity should abort\",\n              \"success\": true\n            }\n          ]\n        },\n        \"scheme-about.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching about:blank with method GET is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:blank with method PUT is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:blank with method POST is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:invalid.com with method GET is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:config with method GET is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:unicorn with method GET is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching about:blank with range header does not affect behavior\",\n              \"success\": true\n            }\n          ]\n        },\n        \"scheme-data.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching data:,response%27s%20body is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching data:,response%27s%20body is OK (same-origin)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching data:,response%27s%20body is OK (cors)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching data:text/plain;base64,cmVzcG9uc2UncyBib[...] is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching data:image/png;base64,cmVzcG9uc2UncyBib2[...] is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [POST] data:,response%27s%20body is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [HEAD] data:,response%27s%20body is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [GET] data:notAdataUrl.com is KO\",\n              \"success\": true\n            }\n          ]\n        },\n        \"text-utf8.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"UTF-8 with BOM with Request.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 with BOM with Response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 with BOM with fetched data (UTF-8 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 with BOM with fetched data (UTF-16 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 with BOM (Response object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 with BOM (Request object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM with Request.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM with Response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM with fetched data (UTF-8 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM with fetched data (UTF-16 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM (Response object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 without BOM (Request object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 with Request.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 with Response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 with fetched data (UTF-8 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 with fetched data (UTF-16 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 (Response object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16BE with BOM decoded as UTF-8 (Request object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 with Request.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 with Response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 with fetched data (UTF-8 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 with fetched data (UTF-16 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 (Response object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16LE with BOM decoded as UTF-8 (Request object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 with Request.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 with Response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 with fetched data (UTF-8 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 with fetched data (UTF-16 charset)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 (Response object)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-16 without BOM decoded as UTF-8 (Request object)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"accept-header.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request through fetch should have 'accept' header with value '*/*'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request through fetch should have 'accept' header with value 'custom/*'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request through fetch should have a 'accept-language' header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request through fetch should have 'accept-language' header with value 'bzh'\",\n              \"success\": true\n            }\n          ]\n        },\n        \"block-mime-as-script.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Should fail loading non-empty script with text/csv MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with audio/aiff MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with audio/midi MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with audio/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with video/avi MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with video/fli MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with video/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with image/jpeg MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with image/gif MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading non-empty script with image/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with text/csv MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with audio/aiff MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with audio/midi MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with audio/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with video/avi MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with video/fli MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with video/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with image/jpeg MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with image/gif MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should fail loading empty script with image/whatever MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should load script with text/html MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should load script with text/plain MIME type\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"conditional-get.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Testing conditional GET with ETags\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (string) \\\"304\\\" but got (object) null\"\n            }\n          ]\n        },\n        \"error-after-response.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Response reader read() promise should reject after a network error happening after resolving fetch promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response reader closed promise should reject after a network error happening after resolving fetch promise\",\n              \"success\": true\n            }\n          ]\n        },\n        \"gc.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"GC/CC should not abruptly close the stream while being consumed by Response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"header-value-combining.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"response.headers.get('content-length') expects 0\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"response.headers.get('content-length') expects 0, 0\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"response.headers.get('double-trouble') expects , \",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"response.headers.get('foo-test') expects 1, 2, 3\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"response.headers.get('heya') expects , \\u000b\\f, 1, , , 2\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"response.headers.get('www-authenticate') expects 1, 2, 3, 4\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            }\n          ]\n        },\n        \"header-value-null-byte.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Ensure fetch() rejects null bytes in headers\",\n              \"success\": true\n            }\n          ]\n        },\n        \"historical.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Headers object no longer has a getAll() method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"'type' getter should not exist on Request objects\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response object no longer has a trailer getter\",\n              \"success\": true\n            }\n          ]\n        },\n        \"http-response-code.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch on 425 response should not be retried for non TLS early data.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"integrity.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Empty string integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-256 integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-384 integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-512 integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-512 integrity with missing padding\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-512 integrity base64url encoded\",\n              \"success\": true\n            },\n            {\n              \"name\": \"SHA-512 integrity base64url encoded with missing padding\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Invalid integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple integrities: valid stronger than invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple integrities: invalid stronger than valid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple integrities: invalid as strong as valid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple integrities: both are valid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple integrities: both are invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS empty integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS SHA-512 integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS invalid integrity\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Empty string integrity for opaque response\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"SHA-* integrity for opaque response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"keepalive.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"[keepalive] simple GET request on 'load' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] simple GET request on 'unload' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] simple GET request on 'pagehide' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] simple POST request on 'load' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] simple POST request on 'unload' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] simple POST request on 'pagehide' [no payload]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"simple keepalive test for web workers;\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        },\n        \"mediasource.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Cannot fetch blob: URL from a MediaSource\",\n              \"success\": false,\n              \"message\": \"MediaSource is not defined\"\n            }\n          ]\n        },\n        \"mode-no-cors.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch ../resources/top.txt with no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch http://web-platform.test:8000/fetch/api/resources/top.txt with no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch https://web-platform.test:8443/fetch/api/resources/top.txt with no-cors mode\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Fetch http://web-platform.test:55268/fetch/api/resources/top.txt with no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            }\n          ]\n        },\n        \"mode-same-origin.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch ../resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch http://web-platform.test:8000/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch https://web-platform.test:8443/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch http://www1.web-platform.test:8000/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch /fetch/api/basic/../resources/redirect.py?location=../resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch /fetch/api/basic/../resources/redirect.py?location=http://web-platform.test:8000/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch /fetch/api/basic/../resources/redirect.py?location=https://web-platform.test:8443/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch /fetch/api/basic/../resources/redirect.py?location=http://www1.web-platform.test:8000/fetch/api/resources/top.txt with same-origin mode\",\n              \"success\": true\n            }\n          ]\n        },\n        \"referrer.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"origin-when-cross-origin policy on a same-origin URL\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request's referrer is correct expected \\\"http://web-platform.test:8000/fetch/api/basic/referrer.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"origin-when-cross-origin policy on a cross-origin URL\",\n              \"success\": true\n            },\n            {\n              \"name\": \"origin-when-cross-origin policy on a cross-origin URL after same-origin redirection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"origin-when-cross-origin policy on a same-origin URL after cross-origin redirection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Referrer with credentials should be stripped\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Referrer with fragment ID should be stripped\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-forbidden-headers.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Accept-Charset is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Accept-Charset does not have the value we defined got disallowed value \\\"utf-8\\\"\"\n            },\n            {\n              \"name\": \"Accept-Encoding is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Accept-Encoding does not have the value we defined got disallowed value \\\"\\\"\"\n            },\n            {\n              \"name\": \"Access-Control-Request-Headers is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Access-Control-Request-Headers does not have the value we defined got disallowed value \\\"\\\"\"\n            },\n            {\n              \"name\": \"Access-Control-Request-Method is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Access-Control-Request-Method does not have the value we defined got disallowed value \\\"\\\"\"\n            },\n            {\n              \"name\": \"Connection is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Connection does not have the value we defined got disallowed value \\\"close\\\"\"\n            },\n            {\n              \"name\": \"Content-Length is a forbidden request header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cookie is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Cookie does not have the value we defined got disallowed value \\\"cookie=none\\\"\"\n            },\n            {\n              \"name\": \"Cookie2 is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Cookie2 does not have the value we defined got disallowed value \\\"cookie2=none\\\"\"\n            },\n            {\n              \"name\": \"Date is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Date does not have the value we defined got disallowed value \\\"Wed, 04 May 1988 22:22:22 GMT\\\"\"\n            },\n            {\n              \"name\": \"DNT is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: DNT does not have the value we defined got disallowed value \\\"4\\\"\"\n            },\n            {\n              \"name\": \"Expect is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Host is a forbidden request header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Keep-Alive is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Origin is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Origin does not have the value we defined got disallowed value \\\"http://wrong-origin.com\\\"\"\n            },\n            {\n              \"name\": \"Referer is a forbidden request header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TE is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: TE does not have the value we defined got disallowed value \\\"trailers\\\"\"\n            },\n            {\n              \"name\": \"Trailer is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Trailer does not have the value we defined got disallowed value \\\"Accept\\\"\"\n            },\n            {\n              \"name\": \"Transfer-Encoding is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Upgrade is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Via is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Via does not have the value we defined got disallowed value \\\"1.1 nowhere.com\\\"\"\n            },\n            {\n              \"name\": \"Proxy- is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Proxy- does not have the value we defined got disallowed value \\\"value\\\"\"\n            },\n            {\n              \"name\": \"Proxy-Test is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Proxy-Test does not have the value we defined got disallowed value \\\"value\\\"\"\n            },\n            {\n              \"name\": \"Sec- is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Sec- does not have the value we defined got disallowed value \\\"value\\\"\"\n            },\n            {\n              \"name\": \"Sec-Test is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Sec-Test does not have the value we defined got disallowed value \\\"value\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value TRACE\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"TRACE\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value TRACK\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"TRACK\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value CONNECT\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"CONNECT\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value trace\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"trace\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value track\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"track\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value connect\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"connect\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method-override does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-http-method does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: x-method-override does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-HTTP-METHOD does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value trace,\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: X-METHOD-OVERRIDE does not have the value we defined got disallowed value \\\"trace,\\\"\"\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value GET,track \",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method-override is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-method-override is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is forbidden to use value  connect\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method-override is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-method-override is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is allowed to use value GETTRACE\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method-override is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-method-override is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is allowed to use value GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method-override is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-http-method is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header x-method-override is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD-OVERRIDE is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-HTTP-METHOD is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            },\n            {\n              \"name\": \"header X-METHOD-OVERRIDE is allowed to use value \\\",TRACE\\\",\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-headers-case.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Multiple headers with the same name, different case (THIS-is-A-test first)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Multiple headers with the same name, different case (THIS-IS-A-TEST first)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-headers-nonascii.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Non-ascii bytes in request headers\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-headers.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch with GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with HEAD\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with PUT without body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with PUT with body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST without body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with text body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with FormData body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Blob body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ArrayBuffer body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Uint8Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Int8Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Float16Array body\",\n              \"success\": false,\n              \"message\": \"Float16Array is not defined\"\n            },\n            {\n              \"name\": \"Fetch with POST with Float32Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Float64Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with DataView body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Blob body with mime type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with Chicken\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with Chicken with body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with GET and mode \\\"cors\\\" does not need an Origin header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST and mode \\\"same-origin\\\" needs an Origin header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST and mode \\\"no-cors\\\" needs an Origin header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with PUT and mode \\\"same-origin\\\" needs an Origin header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with TacO and mode \\\"same-origin\\\" needs an Origin header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with TacO and mode \\\"cors\\\" needs an Origin header\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-private-network-headers.tentative.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Access-Control-Request-Private-Network is a forbidden request header\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Access-Control-Request-Private-Network does not have the value we defined got disallowed value \\\"\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Access-Control-Request-Private-Network: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            }\n          ]\n        },\n        \"request-referrer-redirected-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"request-referrer.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"about:client referrer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"url referrer\",\n              \"success\": false,\n              \"message\": \"assert_equals: request's referer should be: http://web-platform.test:8000/fetch/api/basic/foo expected \\\"http://web-platform.test:8000/fetch/api/basic/foo\\\" but got \\\"http://web-platform.test:8000/fetch/api/basic/request-referrer.any.html\\\"\"\n            }\n          ]\n        },\n        \"response-url.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Testing response url getter with http://web-platform.test:8000/ada\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing response url getter with http://web-platform.test:8000/#\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing response url getter with http://web-platform.test:8000/#ada\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing response url getter with http://web-platform.test:8000#ada\",\n              \"success\": true\n            }\n          ]\n        },\n        \"scheme-blob.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching [GET] URL.createObjectURL(blob) is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [GET] blob:http://www.web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [POST] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [OPTIONS] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [HEAD] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [PUT] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [DELETE] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [INVALID] URL.createObjectURL(blob) is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching [GET] blob:not-backed-by-a-blob/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching URL.createObjectURL(empty_blob) is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching URL.createObjectURL(empty_type_blob) is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching URL.createObjectURL(empty_data_blob) is OK\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching URL.createObjectURL(invalid_type_blob) is OK\",\n              \"success\": false,\n              \"message\": \"assert_equals: Content-Type is invalid expected \\\"\\\" but got \\\"invalid\\\"\"\n            },\n            {\n              \"name\": \"Blob content is not sniffed for a content type [image/png]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Blob content is not sniffed for a content type [text/xml]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Set content type to the empty string for slice with invalid content type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Set content type to the empty string for slice with no content type \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Blob.slice should not sniff the content for a content type\",\n              \"success\": true\n            }\n          ]\n        },\n        \"scheme-others.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching aaa://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching cap://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching cid://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching dav://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching dict://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching dns://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching geo://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching im://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching imap://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching ipp://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching ldap://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching mailto://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching nfs://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching pop://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching rtsp://web-platform.test:8000/ is KO\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetching snmp://web-platform.test:8000/ is KO\",\n              \"success\": true\n            }\n          ]\n        },\n        \"status.h2.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"statusText over H2 for status 200 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 210 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 400 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 404 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 410 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 500 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            },\n            {\n              \"name\": \"statusText over H2 for status 502 should be the empty string\",\n              \"success\": false,\n              \"message\": \"assert_equals: statusText should be the empty string expected \\\"\\\" but got \\\"OMG\\\"\"\n            }\n          ]\n        },\n        \"stream-response.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Stream response's body when content-type is present\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Stream response's body when content-type is not present\",\n              \"success\": true\n            }\n          ]\n        },\n        \"stream-safe-creation.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'fetch'\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'fetch'\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'fetch'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'request'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'response'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'consumeEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'consumeNonEmptyResponse'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'consumeEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.start accessor should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start accessor returning invalid value should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.type accessor should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.type accessor returning invalid value should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.size accessor should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.size accessor returning invalid value should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"throwing Object.prototype.highWaterMark accessor should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.highWaterMark accessor returning invalid value should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Object.prototype.start function which errors the stream should not affect stream creation by 'consumeNonEmptyRequest'\",\n              \"success\": true\n            }\n          ]\n        },\n        \"url-parsing.sub.html?encoding=utf8\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request's referrer uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.redirect() uses the UTF-8 URL parser\",\n              \"success\": true\n            }\n          ]\n        },\n        \"url-parsing.sub.html?encoding=windows-1252\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request's referrer uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.redirect() uses the UTF-8 URL parser\",\n              \"success\": true\n            }\n          ]\n        },\n        \"url-parsing.sub.html?encoding=x-cp1251\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request's referrer uses the UTF-8 URL parser\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response.redirect() uses the UTF-8 URL parser\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-upload.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch with PUT with body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with text body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Blob body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ArrayBuffer body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Uint8Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Int8Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Float16Array body\",\n              \"success\": false,\n              \"message\": \"Float16Array is not defined\"\n            },\n            {\n              \"name\": \"Fetch with POST with Float32Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Float64Array body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with DataView body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with Blob body with mime type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ReadableStream containing String\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ReadableStream containing null\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ReadableStream containing number\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ReadableStream containing ArrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with ReadableStream containing Blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch with POST with text body on 421 response should be retried once on new connection.\",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Streaming upload shouldn't work on Http/1.1.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-upload.h2.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        }\n      },\n      \"request\": {\n        \"request.any.html\": true,\n        \"request-consume.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consume String request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume String request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume String request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume String request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume String request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume ArrayBuffer request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume ArrayBuffer request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume ArrayBuffer request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume ArrayBuffer request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume ArrayBuffer request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Uint8Array request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Uint8Array request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Uint8Array request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Uint8Array request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Uint8Array request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Int8Array request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Int8Array request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Int8Array request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Int8Array request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Int8Array request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Float32Array request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Float32Array request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Float32Array request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Float32Array request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume Float32Array request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume DataView request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume DataView request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume DataView request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume DataView request's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume DataView request's body as JSON\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume FormData request's body as FormData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as json\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as bytes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume blob response's body as blob (empty blob as input)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume JSON from text: '\\\"null\\\"'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume JSON from text: '\\\"1\\\"'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume JSON from text: '\\\"true\\\"'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume JSON from text: '\\\"\\\\\\\"string\\\\\\\"\\\"'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Trying to consume bad JSON text as JSON: 'undefined'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Trying to consume bad JSON text as JSON: '{'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Trying to consume bad JSON text as JSON: 'a'\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Trying to consume bad JSON text as JSON: '['\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-headers.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Adding valid request header \\\"Content-Type: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"Potato: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"proxy: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"proxya: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"sec: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"secb: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"Set-Cookie2: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid request header \\\"User-Agent: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Accept-Charset: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"accept-charset: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"ACCEPT-ENCODING: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Accept-Encoding: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Access-Control-Request-Headers: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Access-Control-Request-Method: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Connection: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Content-Length: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Cookie: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Cookie2: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Date: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"DNT: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Expect: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Host: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Keep-Alive: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Origin: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Referer: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Set-Cookie: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"TE: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Trailer: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Transfer-Encoding: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Upgrade: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Via: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Proxy-: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"proxy-a: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"Sec-: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid request header \\\"sec-b: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"Accept: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"Accept-Language: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-language: OK\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-type: application/x-www-form-urlencoded\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-type: application/x-www-form-urlencoded;charset=UTF-8\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-type: multipart/form-data\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-type: multipart/form-data;charset=UTF-8\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"content-TYPE: text/plain\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding valid no-cors request header \\\"CONTENT-type: text/plain;charset=UTF-8\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"Content-Type: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"Potato: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"proxy: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"proxya: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"sec: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"secb: KO\\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"KO\\\"\"\n            },\n            {\n              \"name\": \"Adding invalid no-cors request header \\\"Empty-Value: \\\"\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"\\\"\"\n            },\n            {\n              \"name\": \"Check that request constructor is filtering headers provided as init parameter\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"potato\\\"\"\n            },\n            {\n              \"name\": \"Check that no-cors request constructor is filtering headers provided as init parameter\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"potato\\\"\"\n            },\n            {\n              \"name\": \"Check that no-cors request constructor is filtering headers provided as part of request parameter\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected (object) null but got (string) \\\"potato\\\"\"\n            },\n            {\n              \"name\": \"Request should get its content-type from the init request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should not get its content-type from the init request if init headers are provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should get its content-type from the body if none is provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should get its content-type from init headers if one is provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing request header creations with various objects\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing empty Request Content-Type header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Test that Request.headers has the [SameObject] extended attribute\",\n              \"success\": true\n            }\n          ]\n        },\n        \"destination\": {\n          \"fetch-destination-frame.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"frame fetches with a \\\"frame\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"fetch-destination-iframe.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"iframe fetches with a \\\"iframe\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"fetch-destination-no-load-event.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"Background image fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"SVG use element fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"Font loading API fetches with an \\\"font\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"CSS font fetches with an \\\"font\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"sendBeacon() fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"Cache.add() fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"importScripts() fetches with a \\\"script\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"@import fetches with a \\\"style\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              }\n            ]\n          },\n          \"fetch-destination-prefetch.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=prefetch fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              }\n            ]\n          },\n          \"fetch-destination-worker.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"DedicatedWorker fetches with a \\\"worker\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"SharedWorker fetches with a \\\"sharedworker\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              }\n            ]\n          },\n          \"fetch-destination.https.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Initialize global state\",\n                \"success\": false,\n                \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n              },\n              {\n                \"name\": \"HTMLImageElement fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLImageElement with srcset attribute fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLImageElement with a HTMLPictureElement parent attribute fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"SVGImageElement fetches with an \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"fetch() fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"XMLHttpRequest() fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"EventSource() fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLAudioElement fetches with an \\\"audio\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLVideoElement fetches with a \\\"video\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLScriptElement fetches with a \\\"script\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"AudioWorklet module fetches with a \\\"audioworklet\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=stylesheet fetches with a \\\"style\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"Import declaration with `type: \\\"css\\\"` fetches with a \\\"style\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"Import declaration with `type: \\\"json\\\"` fetches with a \\\"json\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=fetch fetches with an empty string Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=style fetches with a \\\"style\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=json fetches with a \\\"json\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=script fetches with a \\\"script\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=font fetches with a \\\"font\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=image fetches with a \\\"image\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=audio fetches with a \\\"audio\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=video fetches with a \\\"video\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=track fetches with a \\\"track\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=document fetches with a \\\"document\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=worker fetches with a \\\"worker\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=sharedworker fetches with a \\\"sharedworker\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=xslt fetches with a \\\"xslt\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              },\n              {\n                \"name\": \"HTMLLinkElement with rel=preload and as=manifest fetches with a \\\"manifest\\\" Request.destination\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Fetch errored. Reached unreachable code\"\n              }\n            ]\n          }\n        },\n        \"forbidden-method.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request() with a forbidden method CONNECT must throw.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request() with a forbidden method TRACE must throw.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request() with a forbidden method TRACK must throw.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request() with a forbidden method connect must throw.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request() with a forbidden method trace must throw.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request() with a forbidden method track must throw.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"multi-globals\": {\n          \"construct-in-detached-frame.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"creating a request from another request in a detached realm should work\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"url-parsing.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"should parse the URL relative to the current settings object\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.addEventListener is not a function\\\"\"\n              }\n            ]\n          }\n        },\n        \"request-bad-port.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request on bad port 0 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 1 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 7 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 9 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 11 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 13 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 15 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 17 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 19 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 20 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 21 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 22 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 23 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 25 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 37 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 42 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 43 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 53 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 69 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 77 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 79 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 87 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 95 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 101 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 102 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 103 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 104 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 109 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 110 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 111 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 113 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 115 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 117 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 119 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 123 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 135 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 137 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 139 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 143 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 161 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 179 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 389 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 427 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 465 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 512 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 513 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 514 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 515 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 526 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 530 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 531 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 532 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 540 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 548 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 554 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 556 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 563 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 587 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 601 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 636 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 989 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 990 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 993 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 995 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 1719 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 1720 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 1723 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 2049 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 3659 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 4045 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 4190 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 5060 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 5061 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6000 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6566 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6665 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6666 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6667 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6668 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6669 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6679 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 6697 should throw TypeError.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request on bad port 10080 should throw TypeError.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-cache-default-conditional.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Modified-Since header is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-None-Match header is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Unmodified-Since header is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Match header is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header (following a request without additional headers) is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header is treated similarly to \\\"no-store\\\" with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header is treated similarly to \\\"no-store\\\" with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header is treated similarly to \\\"no-store\\\" with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode with an If-Range header is treated similarly to \\\"no-store\\\" with Last-Modified and fresh response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-cache-default.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode checks the cache for previously cached content and goes to the network for stale responses with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode checks the cache for previously cached content and goes to the network for stale responses with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode checks the cache for previously cached content and avoids going to the network if a fresh response exists with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"default\\\" mode checks the cache for previously cached content and avoids going to the network if a fresh response exists with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"Responses with the \\\"Cache-Control: no-store\\\" header are not stored in the cache with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Responses with the \\\"Cache-Control: no-store\\\" header are not stored in the cache with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Responses with the \\\"Cache-Control: no-store\\\" header are not stored in the cache with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Responses with the \\\"Cache-Control: no-store\\\" header are not stored in the cache with Last-Modified and fresh response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-cache-force-cache.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and avoid revalidation for stale responses with Etag and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and avoid revalidation for stale responses with Last-Modified and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and avoid revalidation for fresh responses with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and avoid revalidation for fresh responses with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response is not found with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response is not found with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response is not found with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response is not found with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response would vary with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response would vary with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response would vary with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" mode checks the cache for previously cached content and goes to the network if a cached response would vary with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" stores the response in the cache if it goes to the network with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" stores the response in the cache if it goes to the network with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" stores the response in the cache if it goes to the network with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"force-cache\\\" stores the response in the cache if it goes to the network with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            }\n          ]\n        },\n        \"request-cache-no-cache.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"no-cache\\\" mode revalidates stale responses found in the cache with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-cache\\\" mode revalidates stale responses found in the cache with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-cache\\\" mode revalidates fresh responses found in the cache with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-cache\\\" mode revalidates fresh responses found in the cache with Last-Modified and fresh response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-cache-no-store.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not check the cache for previously cached content and goes to the network regardless with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not check the cache for previously cached content and goes to the network regardless with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not store the response in the cache with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not store the response in the cache with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not store the response in the cache with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"no-store\\\" mode does not store the response in the cache with Last-Modified and fresh response\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-cache-only-if-cached.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and avoids revalidation for stale responses with Etag and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and avoids revalidation for stale responses with Last-Modified and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and avoids revalidation for fresh responses with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and avoids revalidation for fresh responses with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and does not go to the network if a cached response is not found with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_true: fetch should have been an error expected true got false\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" mode checks the cache for previously cached content and does not go to the network if a cached response is not found with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_true: fetch should have been an error expected true got false\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") uses cached same-origin redirects to same-origin content with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 4\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") uses cached same-origin redirects to same-origin content with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 4\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") uses cached same-origin redirects to same-origin content with Etag and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 4\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") uses cached same-origin redirects to same-origin content with Last-Modified and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 4\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") does not follow redirects across origins and rejects with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") does not follow redirects across origins and rejects with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") does not follow redirects across origins and rejects with Etag and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            },\n            {\n              \"name\": \"RequestCache \\\"only-if-cached\\\" (with \\\"same-origin\\\") does not follow redirects across origins and rejects with Last-Modified and stale response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            }\n          ]\n        },\n        \"request-cache-reload.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does not check the cache for previously cached content and goes to the network regardless with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does not check the cache for previously cached content and goes to the network regardless with Last-Modified and fresh response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 1 but got 2\"\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache even if a previous response is already stored with Etag and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache even if a previous response is already stored with Last-Modified and stale response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache even if a previous response is already stored with Etag and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            },\n            {\n              \"name\": \"RequestCache \\\"reload\\\" mode does store the response in the cache even if a previous response is already stored with Last-Modified and fresh response\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 2 but got 3\"\n            }\n          ]\n        },\n        \"request-clone.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check cloning a request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cloning a request copies the headers\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-constructor-init-body-override.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check that the body of a new request can be overridden when created from an existing Request object\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check that the body of a new request can be duplicated from an existing Request object\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-consume-empty.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consume request's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as json (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as formData with correct multipart type (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as formData with correct urlencoded type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume request's body as formData without correct type (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty blob request body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty text request body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty blob request body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty text request body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty URLSearchParams request body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty FormData request body as text\",\n              \"success\": false,\n              \"message\": \"assert_equals: Resolved value should be empty expected 0 but got 38\"\n            },\n            {\n              \"name\": \"Consume empty ArrayBuffer request body as text\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-disturbed.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request's body: initial state\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request without body cannot be disturbed\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cloning a disturbed request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check creating a new request from a disturbed request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check creating a new request with a new body from a disturbed request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Input request used for creating new request became disturbed\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Input request used for creating new request became disturbed even if body is not used\",\n              \"success\": false,\n              \"message\": \"assert_true: bodyUsed is true when request is disturbed expected true got false\"\n            },\n            {\n              \"name\": \"Check consuming a disturbed request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request construction failure should not set \\\"bodyUsed\\\"\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-error.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"RequestInit's window is not null\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Input URL is not valid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Input URL has credentials\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's mode is navigate\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's referrer is invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's method is invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's method is forbidden\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's mode is no-cors and method is not simple\",\n              \"success\": true\n            },\n            {\n              \"name\": \"RequestInit's cache mode is only-if-cached and mode is not same-origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request with cache mode: only-if-cached and fetch mode cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request with cache mode: only-if-cached and fetch mode no-cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Bad referrerPolicy init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Bad mode init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Bad credentials init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Bad cache init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Bad redirect init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"request-error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should get its content-type from the init request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should not get its content-type from the init request if init headers are provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should get its content-type from the body if none is provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request should get its content-type from init headers if one is provided\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request with cache mode: only-if-cached and fetch mode: same-origin\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-001.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check method init value of GET and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of HEAD and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of POST and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of PUT and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of DELETE and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of OPTIONS and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of get and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of head and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of post and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of put and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of delete and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of options and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of Get and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of hEad and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of poSt and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of Put and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of deleTe and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of optionS and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of PATCH and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of patch and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method init value of patCh and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of /relative/ressource and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of http://web-platform.test:8000/relative/ressource?query=true#fragment and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of http://web-platform.test:8000/ and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of http://test.url and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of about:client and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer init value of  and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of  and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of no-referrer and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of no-referrer-when-downgrade and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of origin-when-cross-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of unsafe-url and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of same-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of strict-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy init value of strict-origin-when-cross-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check mode init value of same-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check mode init value of no-cors and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check mode init value of cors and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check credentials init value of omit and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check credentials init value of same-origin and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check credentials init value of include and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache init value of default and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache init value of no-store and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache init value of reload and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache init value of no-cache and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache init value of force-cache and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check redirect init value of follow and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check redirect init value of error and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check redirect init value of manual and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check integrity init value of  and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check integrity init value of AZERTYUIOP1234567890 and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check window init value of null and associated getter\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-002.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Initialize Request with headers values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"undefined\\\", undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"null\\\", null\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"[object Blob]\\\", application/octet-binary\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"[object FormData]\\\", multipart/form-data\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"This is a USVString\\\", text/plain;charset=UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"hi!\\\", text/plain;charset=UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Request's body with \\\"name=value\\\", application/x-www-form-urlencoded;charset=UTF-8\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-003.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check request values when initialized from Request\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer attribute expected \\\"http://web-platform.test:8000/\\\" but got \\\"about:client\\\"\"\n            },\n            {\n              \"name\": \"Check request values when initialized from Request and init values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check request values when initialized from url string\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check request values when initialized from url and init values\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-contenttype.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Default Content-Type for Request with empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with Blob body (no type set)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with Blob body (empty type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with Blob body (set type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with buffer source body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with FormData body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with string body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Request with ReadableStream body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with Blob body (no type set)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with Blob body (empty type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with Blob body (set type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with buffer source body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with FormData body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with string body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Request with ReadableStream body\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-priority.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"new Request() with a 'high' priority does not throw an error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new Request() with a 'low' priority does not throw an error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new Request() with a 'auto' priority does not throw an error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new Request() throws a TypeError if any of RequestInit's members' values are invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetch() with a 'high' priority completes successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetch() with a 'low' priority completes successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetch() with a 'auto' priority completes successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetch() with an invalid priority returns a rejected promise with a TypeError\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-init-stream.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Constructing a Request with a stream holds the original object.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a stream on which getReader() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a stream on which read() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a stream on which read() and releaseLock() are called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a Request on which body.getReader() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a Request on which body.getReader().read() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Request with a Request on which read() and releaseLock() are called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to omit .duplex when the body is null.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to omit .duplex when the body is a string.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to omit .duplex when the body is a Uint8Array.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to omit .duplex when the body is a Blob.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to omit .duplex when the body is a ReadableStream.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to set .duplex = 'half' when the body is null.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to set .duplex = 'half' when the body is a string.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to set .duplex = 'half' when the body is a Uint8Array.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to set .duplex = 'half' when the body is a Blob.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to set .duplex = 'half' when the body is a ReadableStream.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to set .duplex = 'full' when the body is null.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to set .duplex = 'full' when the body is a string.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to set .duplex = 'full' when the body is a Uint8Array.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to set .duplex = 'full' when the body is a Blob.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is error to set .duplex = 'full' when the body is a ReadableStream.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"It is OK to omit duplex when init.body is not given and input.body is given.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-keepalive-quota.html?include=fast\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A Keep-Alive fetch() with a small body should succeed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"A Keep-Alive fetch() with a body at the Quota Limit should succeed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"A Keep-Alive fetch() with a body over the Quota Limit should reject.\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"request-keepalive-quota.html?include=slow-1\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A Keep-Alive fetch() should return its allocated Quota upon promise resolution.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-keepalive-quota.html?include=slow-2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A Keep-Alive fetch() should return only its allocated Quota upon promise resolution.\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"request-keepalive-quota.html?include=slow-3\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A Keep-Alive fetch() should not be allowed if the Quota is used up.\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"request-keepalive.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"keepalive flag\",\n              \"success\": true\n            },\n            {\n              \"name\": \"keepalive flag with stream body\",\n              \"success\": true\n            }\n          ]\n        },\n        \"request-reset-attributes.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request.isReloadNavigation is reset with non-empty RequestInit\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'getRegistration')\\\"\"\n            },\n            {\n              \"name\": \"Request.isHistoryNavigation is reset with non-empty RequestInit\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'getRegistration')\\\"\"\n            },\n            {\n              \"name\": \"Request.mode is reset with non-empty RequestInit when it's \\\"navigate\\\"\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'getRegistration')\\\"\"\n            }\n          ]\n        },\n        \"request-structure.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request has clone method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request has arrayBuffer method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request has blob method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request has formData method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request has json method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request has text method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check method attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check url attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check destination attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrer attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check referrerPolicy attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check mode attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check credentials attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cache attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check redirect attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check integrity attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check isReloadNavigation attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check isHistoryNavigation attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check duplex attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check bodyUsed attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request does not expose priority attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request does not expose internalpriority attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request does not expose blocking attribute\",\n              \"success\": true\n            }\n          ]\n        },\n        \"url-encoding.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"URL encoding and Request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"URL encoding and fetch()\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"response\": {\n        \"response.any.html\": true,\n        \"response-consume.any.html\": true,\n        \"response-json.any.html\": true,\n        \"json.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Ensure the correct JSON parser is used\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Ensure UTF-16 results in an error\",\n              \"success\": true\n            }\n          ]\n        },\n        \"multi-globals\": {\n          \"url-parsing.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"should parse the redirect Location URL relative to the current settings object\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.addEventListener is not a function\\\"\"\n              }\n            ]\n          }\n        },\n        \"response-arraybuffer-realm.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"realm of the ArrayBuffer from Response arrayBuffer()\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: onload is not defined\\\"\"\n            }\n          ]\n        },\n        \"response-blob-realm.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"realm of the Uint8Array from Response bytes()\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: onload is not defined\\\"\"\n            }\n          ]\n        },\n        \"response-body-read-task-handling.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"reading from a body stream should occur in a microtask scope\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: terminated\\\"\"\n            },\n            {\n              \"name\": \"piping from a body stream to a JS-written WritableStream should occur in a microtask scope\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: terminated\\\"\"\n            }\n          ]\n        },\n        \"response-cancel-stream.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Cancelling a starting blob Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling a loading blob Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling a closed blob Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling a starting Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling a loading Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling a closed Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Accessing .body after canceling it\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-clone-iframe.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clone within removed iframe should not crash\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: onload is not defined\\\"\"\n            }\n          ]\n        },\n        \"response-clone.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check Response's clone with default values, without body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check Response's clone has the expected attribute values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check original response's body after cloning\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check cloned response's body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cannot clone a disturbed response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cloned responses should provide the same data\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cancelling stream should not affect cloned one\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Int8Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Int16Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Int32Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (ArrayBufferchunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"[object ArrayBuffer]\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Uint8Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Uint8ClampedArraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Uint16Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Uint32Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (BigInt64Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (BigUint64Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Float16Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_array_equals: Cloned buffer chunks have the same content value is undefined, expected array\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Float32Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0,0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (Float64Arraychunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"0,0\\\"\"\n            },\n            {\n              \"name\": \"Check response clone use structureClone for teed ReadableStreams (DataViewchunk)\",\n              \"success\": false,\n              \"message\": \"assert_not_equals: Buffer of cloned response stream is a clone of the original buffer got disallowed value object \\\"[object DataView]\\\"\"\n            }\n          ]\n        },\n        \"response-consume-empty.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consume response's body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as json (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as formData with correct multipart type (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as formData with correct urlencoded type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body as formData without correct type (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty blob response body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty text response body as arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty blob response body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty text response body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty URLSearchParams response body as text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty FormData response body as text\",\n              \"success\": false,\n              \"message\": \"assert_equals: Resolved value should be empty expected 0 but got 38\"\n            },\n            {\n              \"name\": \"Consume empty ArrayBuffer response body as text\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-consume-stream.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Read empty text response's body as readableStream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read empty blob response's body as readableStream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read blob response's body as readableStream with mode=undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read text response's body as readableStream with mode=undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read URLSearchParams response's body as readableStream with mode=undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read array buffer response's body as readableStream with mode=undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read form data response's body as readableStream with mode=undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read blob response's body as readableStream with mode=byob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read text response's body as readableStream with mode=byob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read URLSearchParams response's body as readableStream with mode=byob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read array buffer response's body as readableStream with mode=byob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read form data response's body as readableStream with mode=byob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting an error Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a redirect Response stream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Reading with offset from Response stream\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-consume.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consume response's body: from text to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text to blob\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from text to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text to json\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text with correct multipart type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text with correct multipart type to formData with BOM\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text without correct multipart type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text with correct urlencoded type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from text without correct urlencoded type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob to blob\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from blob to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob to json\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob with correct multipart type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob without correct multipart type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob with correct urlencoded type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from blob without correct urlencoded type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from FormData to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from FormData without correct type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from FormData to blob\",\n              \"success\": false,\n              \"message\": \"assert_equals: Blob body type should be computed from the response Content-Type expected \\\"multipart/form-data; boundary=----formdata-undici-084518311125\\\" but got \\\"multipart/form-data;boundary=----formdata-undici-084518311125\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from FormData to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from FormData to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from URLSearchParams to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from URLSearchParams without correct type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from URLSearchParams to blob\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from URLSearchParams to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from URLSearchParams to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream to blob\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from stream to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream to json\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream with correct multipart type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream without correct multipart type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream with correct urlencoded type to formData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from stream without correct urlencoded type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from fetch to blob\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            },\n            {\n              \"name\": \"Consume response's body: from fetch to text\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from fetch to arrayBuffer\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from fetch without correct type to formData (error case)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume response's body: from multipart form data blob to formData\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: FileReader is not defined\\\"\"\n            }\n          ]\n        },\n        \"response-error-from-stream.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ReadableStreamDefaultReader Promise receives ReadableStream start() Error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStreamDefaultReader Promise receives ReadableStream pull() Error\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.arrayBuffer() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.blob() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.bytes() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.formData() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.json() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream start() Error propagates to Response.text() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.arrayBuffer() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.blob() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.bytes() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.formData() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.json() Promise\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream pull() Error propagates to Response.text() Promise\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-error.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Throws RangeError when responseInit's status is 0\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws RangeError when responseInit's status is 100\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws RangeError when responseInit's status is 199\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws RangeError when responseInit's status is 600\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws RangeError when responseInit's status is 1000\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when responseInit's statusText is \\n\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when responseInit's statusText is Ā\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when building a response with body and a body status of 204\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when building a response with body and a body status of 205\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when building a response with body and a body status of 304\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-from-stream.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Constructing a Response with a stream on which getReader() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Response with a stream on which read() is called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Constructing a Response with a stream on which read() and releaseLock() are called\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-headers-guard.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Ensure response headers are immutable\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-init-001.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check default value for type attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check default value for url attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check default value for ok attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check default value for status attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check default value for statusText attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check default value for body attribute\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check status init values and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check statusText init values and associated getter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Test that Response.headers has the [SameObject] extended attribute\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-init-002.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Initialize Response with headers values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Response's body with application/octet-binary\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Response's body with multipart/form-data\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Response's body with application/x-www-form-urlencoded;charset=UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Initialize Response's body with text/plain;charset=UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Read Response's body as readableStream\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing empty Response Content-Type header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing null Response body\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-init-contenttype.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Default Content-Type for Response with empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with Blob body (no type set)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with Blob body (empty type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with Blob body (set type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with buffer source body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with FormData body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with string body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Default Content-Type for Response with ReadableStream body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with Blob body (no type set)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with Blob body (empty type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with Blob body (set type)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with buffer source body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with FormData body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with URLSearchParams body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with string body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Can override Content-Type for Response with ReadableStream body\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-static-error.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check response returned by static method error()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"the 'guard' of the Headers instance should be immutable\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-static-json.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check response returned by static json() with init undefined\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with init {\\\"status\\\":400}\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with init {\\\"statusText\\\":\\\"foo\\\"}\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with init {\\\"headers\\\":{}}\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with init {\\\"headers\\\":{\\\"content-type\\\":\\\"foo/bar\\\"}}\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with init {\\\"headers\\\":{\\\"x-foo\\\":\\\"bar\\\"}}\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when calling static json() with a status of 204\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when calling static json() with a status of 205\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Throws TypeError when calling static json() with a status of 304\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check static json() encodes JSON objects correctly\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check static json() throws when data is not encodable\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check static json() throws when data is circular\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check static json() propagates JSON serializer errors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with input 𝌆\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with input U+df06U+d834\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static json() with input U+dead\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-static-redirect.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Check default redirect response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static method redirect(), status = 301\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static method redirect(), status = 302\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static method redirect(), status = 303\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static method redirect(), status = 307\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check response returned by static method redirect(), status = 308\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check error returned when giving invalid url to redirect()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check error returned when giving invalid status to redirect(), status = 200\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check error returned when giving invalid status to redirect(), status = 309\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check error returned when giving invalid status to redirect(), status = 400\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check error returned when giving invalid status to redirect(), status = 500\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-bad-chunk.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.arrayBuffer() causes TypeError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.blob() causes TypeError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.bytes() causes TypeError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.formData() causes TypeError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.json() causes TypeError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ReadableStream with non-Uint8Array chunk passed to Response.text() causes TypeError\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-1.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Getting blob after getting the Response body - not disturbed, not locked (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting the Response body - not disturbed, not locked (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting the Response body - not disturbed, not locked (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting the Response body - not disturbed, not locked (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after getting the Response body - not disturbed, not locked (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting the Response body - not disturbed, not locked (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting the Response body - not disturbed, not locked (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting the Response body - not disturbed, not locked (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after getting the Response body - not disturbed, not locked (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting the Response body - not disturbed, not locked (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting the Response body - not disturbed, not locked (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting the Response body - not disturbed, not locked (body source: string)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-2.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Getting blob after getting a locked Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting a locked Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting a locked Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting a locked Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after getting a locked Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting a locked Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting a locked Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting a locked Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after getting a locked Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after getting a locked Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after getting a locked Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after getting a locked Response body (body source: string)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-3.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Getting blob after reading the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after reading the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after reading the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after reading the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after reading the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after reading the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after reading the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after reading the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after reading the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after reading the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after reading the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after reading the Response body (body source: string)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-4.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Getting blob after cancelling the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after cancelling the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after cancelling the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after cancelling the Response body (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after cancelling the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after cancelling the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after cancelling the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after cancelling the Response body (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting blob after cancelling the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting text after cancelling the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting json after cancelling the Response body (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting arrayBuffer after cancelling the Response body (body source: string)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-5.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Getting a body reader after consuming as blob (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as text (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as json (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as arrayBuffer (body source: fetch)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as blob (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as text (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as json (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as arrayBuffer (body source: stream)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as blob (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as text (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as json (body source: string)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Getting a body reader after consuming as arrayBuffer (body source: string)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-6.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A non-closed stream on which read() has been called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"A non-closed stream on which cancel() has been called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"A closed stream on which read() has been called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"An errored stream on which read() has been called\",\n              \"success\": true\n            },\n            {\n              \"name\": \"An errored stream on which cancel() has been called\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-disturbed-by-pipe.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"using pipeTo on Response body should disturb it synchronously\",\n              \"success\": true\n            },\n            {\n              \"name\": \"using pipeThrough on Response body should disturb it synchronously\",\n              \"success\": true\n            }\n          ]\n        },\n        \"response-stream-with-broken-then.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Attempt to inject {done: false, value: bye} via Object.prototype.then.\",\n              \"success\": false,\n              \"message\": \"assert_equals: The value should be \\\"hello\\\". expected \\\"hello\\\" but got \\\"bye\\\"\"\n            },\n            {\n              \"name\": \"Attempt to inject value: undefined via Object.prototype.then.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Received non-Uint8Array chunk\\\"\"\n            },\n            {\n              \"name\": \"Attempt to inject undefined via Object.prototype.then.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot destructure property 'done' of '(intermediate value)' as it is undefined.\\\"\"\n            },\n            {\n              \"name\": \"Attempt to inject 8.2 via Object.prototype.then.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Received non-Uint8Array chunk\\\"\"\n            },\n            {\n              \"name\": \"intercepting arraybuffer to text conversion via Object.prototype.then should not be possible\",\n              \"success\": false,\n              \"message\": \"assert_equals: The value should be \\\"hello\\\". expected \\\"hello\\\" but got \\\"bye\\\"\"\n            },\n            {\n              \"name\": \"intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"headers\": {\n        \"headers-basic.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Create headers from no parameter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers from undefined parameter\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers from empty object\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with null should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with 1 should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with sequence\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with record\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with existing headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers with existing headers with custom iterator\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check append method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check set method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check has method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check delete method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check get method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check keys method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check values method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check entries method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check Symbol.iterator method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check forEach method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Iteration skips elements removed while iterating\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Removing elements already iterated over causes an element to be skipped during iteration\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Appending a value pair during iteration causes it to be reached during iteration\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Prepending a value pair before the current element position causes it to be skipped during iteration and adds the current element a second time\",\n              \"success\": true\n            }\n          ]\n        },\n        \"headers-combine.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Create headers using same name for different values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check delete and has methods when using same name for different values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check set methods when called with already used name\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check append methods when called with already used name\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Iterate combined values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Iterate combined values in sorted order\",\n              \"success\": true\n            }\n          ]\n        },\n        \"headers-normalize.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Create headers with not normalized values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check append method with not normalized values\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check set method with not normalized values\",\n              \"success\": true\n            }\n          ]\n        },\n        \"header-setcookie.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Headers.prototype.get combines set-cookie headers in order\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator does not combine set-cookie headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator does not special case set-cookie2 headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator does not combine set-cookie & set-cookie2 headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator preserves set-cookie ordering\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator preserves per header ordering, but sorts keys alphabetically\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator preserves per header ordering, but sorts keys alphabetically (and ignores value ordering)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator is correctly updated with set-cookie changes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers iterator is correctly updated with set-cookie changes #2\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.has works for set-cookie\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.append works for set-cookie\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.set works for set-cookie\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.delete works for set-cookie\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with no headers present\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with one header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with one header created from an object\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with multiple headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with an empty header\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie with two equal headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie ignores set-cookie2 headers\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers.prototype.getSetCookie preserves header ordering\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding Set-Cookie headers normalizes their value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Adding invalid Set-Cookie headers throws\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Set-Cookie is a forbidden response header\",\n              \"success\": false,\n              \"message\": \"assert_array_equals: lengths differ, expected array [] length 0, got [\\\"foo=bar\\\"] length 1\"\n            }\n          ]\n        },\n        \"header-values-normalize.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"header-values.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"headers-casing.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Create headers, names use characters with different case\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check append method, names use characters with different case\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check set method, names use characters with different case\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check delete method, names use characters with different case\",\n              \"success\": true\n            }\n          ]\n        },\n        \"headers-errors.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Create headers giving an array having one string as init argument\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers giving an array having three strings as init argument\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers giving bad header name as init argument\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Create headers giving bad header value as init argument\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers get with an invalid name invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers get with an invalid name [object Object]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers delete with an invalid name invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers delete with an invalid name [object Object]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers has with an invalid name invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers has with an invalid name [object Object]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers set with an invalid name invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers set with an invalid name [object Object]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers set with an invalid value invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers append with an invalid name invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers append with an invalid name [object Object]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Check headers append with an invalid value invalidĀ\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers forEach throws if argument is not callable\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers forEach loop should stop if callback is throwing exception\",\n              \"success\": true\n            }\n          ]\n        },\n        \"headers-no-cors.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Loading data…\",\n              \"success\": true\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept set to sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\" but got \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, \\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept-language set to sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\" but got \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, \\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-language set to sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\" but got \\\"sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, \\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept set to , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"\\\" but got \\\", sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept-language set to , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"\\\" but got \\\", sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-language set to , sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"\\\" but got \\\", sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-type set to text/plain;ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, text/plain\",\n              \"success\": false,\n              \"message\": \"assert_equals: 1 expected \\\"text/plain;ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\\\" but got \\\"text/plain;ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss, text/plain\\\"\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept/\\\" as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept/012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept-language/\\u0001 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have accept-language/@ as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have authorization/basics as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-language/\\u0001 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-language/@ as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-type/text/html as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have content-type/text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have range/bytes 0- as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have test/hi as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have dpr/2 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have rtt/1.0 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have downlink/-1.0 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have ect/6g as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have save-data/on as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have viewport-width/100 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have width/100 as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            },\n            {\n              \"name\": \"\\\"no-cors\\\" Headers object cannot have unknown/doesitmatter as header\",\n              \"success\": false,\n              \"message\": \"assert_false: expected false got true\"\n            }\n          ]\n        },\n        \"headers-record.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Passing nothing to Headers constructor\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Passing undefined to Headers constructor\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Passing null to Headers constructor\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Basic operation with one property\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Basic operation with one property and a proto\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with two properties\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with two properties one of which has an invalid name\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with two properties one of which has an invalid value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with non-enumerable properties\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with undefined descriptors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Correct operation ordering with repeated keys\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Basic operation with Symbol keys\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Operation with non-enumerable Symbol keys\",\n              \"success\": true\n            }\n          ]\n        },\n        \"headers-structure.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Headers has append method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has delete method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has get method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has has method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has set method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has entries method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has keys method\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Headers has values method\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"abort\": {\n        \"cache.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Signals are not stored in the cache API\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signals are not stored in the cache API, even if they're already aborted\",\n              \"success\": true\n            }\n          ]\n        },\n        \"destroyed-context.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"general.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Aborting rejects with AbortError\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting rejects with abort reason\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting rejects with AbortError - no-cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's window is not null\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Input URL is not valid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Input URL has credentials\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's mode is navigate\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's referrer is invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's method is invalid\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's method is forbidden\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's mode is no-cors and method is not simple\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - RequestInit's cache mode is only-if-cached and mode is not same-origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode no-cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Bad referrerPolicy init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Bad mode init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Bad credentials init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Bad cache init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"TypeError from request constructor takes priority - Bad redirect init parameter value\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request objects have a signal property\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal on request object\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal on request object should also have abort reason\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal on request object created from request object\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal on request object created from request object, with signal on second request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal on request object created from request object, with signal on second request overriding another\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal retained after unrelated properties are overridden by fetch\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal removed by setting to null\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Already aborted signal rejects immediately\",\n              \"success\": false,\n              \"message\": \"assert_array_equals: expected property 0 to be \\\"fetch-reject\\\" but got \\\"next-microtask\\\" (expected array [\\\"fetch-reject\\\", \\\"next-microtask\\\"] got [\\\"next-microtask\\\", \\\"fetch-reject\\\"])\"\n            },\n            {\n              \"name\": \"Request is still 'used' if signal is aborted before fetching\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.arrayBuffer() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.blob() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.bytes() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.formData() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.json() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"response.text() rejects if already aborted\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Call text() twice on aborted response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Already aborted signal does not make request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Already aborted signal can be used for many fetches\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal can be used to abort other fetches, even if another fetch succeeded before aborting\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Underlying connection is closed when aborting after receiving response\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Underlying connection is closed when aborting after receiving response - no-cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.arrayBuffer()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.blob()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.bytes()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.formData()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.json()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch aborted & connection closed when aborted after calling response.text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Stream errors once aborted. Underlying connection closed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Stream errors once aborted, after reading. Underlying connection closed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Stream will not error if body is empty. It's closed with an empty queue before it errors.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Readable stream synchronously cancels with AbortError if aborted before reading\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Signal state is cloned\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Clone aborts with original controller\",\n              \"success\": true\n            }\n          ]\n        },\n        \"keepalive.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"aborting a keepalive fetch should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"aborting a detached keepalive fetch should not do anything\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"request.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Calling arrayBuffer() on an aborted request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting a request after calling arrayBuffer()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling arrayBuffer() on an aborted consumed empty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling arrayBuffer() on an aborted consumed nonempty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling blob() on an aborted request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting a request after calling blob()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling blob() on an aborted consumed empty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling blob() on an aborted consumed nonempty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling formData() on an aborted request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting a request after calling formData()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling formData() on an aborted consumed nonempty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling json() on an aborted request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting a request after calling json()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling json() on an aborted consumed nonempty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling text() on an aborted request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Aborting a request after calling text()\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling text() on an aborted consumed empty request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Calling text() on an aborted consumed nonempty request\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"body\": {\n        \"formdata.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consume empty response.formData() as FormData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume empty request.formData() as FormData\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consume multipart/form-data headers case-insensitively\",\n              \"success\": true\n            }\n          ]\n        },\n        \"mime-type.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Request: overriding explicit Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: overriding explicit Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: removing implicit Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: removing implicit Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: setting missing Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: setting missing Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: MIME type for Blob from empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: MIME type for Blob from empty body\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: MIME type for Blob from empty body with Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: MIME type for Blob from empty body with Content-Type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: MIME type for Blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: MIME type for Blob\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: MIME type for Blob with non-empty type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: MIME type for Blob with non-empty type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: Extract a MIME type with clone\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: Extract a MIME type with clone\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: Content-Type in headers wins Blob\\\"s type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: Content-Type in headers wins Blob\\\"s type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Request: setting missing Content-Type in headers and it wins Blob\\\"s type\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Response: setting missing Content-Type in headers and it wins Blob\\\"s type\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"cors\": {\n        \"cors-basic.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Same domain different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"Same domain different port [server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Same domain different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"Same domain different protocol different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Same domain different protocol different port [server forbid CORS]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same domain different protocol different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Cross domain basic usage [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"Cross domain basic usage [server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Cross domain basic usage [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"Cross domain different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"Cross domain different port [server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Cross domain different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"Cross domain different protocol [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Cross domain different protocol [server forbid CORS]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain different protocol [cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            }\n          ]\n        },\n        \"cors-cookies-redirect.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Set cookies\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing credentials after cross-origin redirection with CORS and no preflight\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request includes cookie(s) expected (string) \\\"a=2\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Testing credentials after cross-origin redirection with CORS and preflight\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request includes cookie(s) expected (string) \\\"a=2\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Clean cookies\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-cookies.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Omit mode: no cookie sent\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Include mode: 1 cookie\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request includes cookie(s) expected (string) \\\"a=1\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Include mode: local cookies are not sent with remote request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Include mode: remote cookies are not sent with local request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same-origin mode: cookies are discarded in cors request\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Include mode: remote cookies are not sent with other remote request\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-expose-star.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Basic Access-Control-Expose-Headers: * support\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"* for credentialed fetches only matches literally\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"* can be one of several values\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"cors\\\" but got \\\"basic\\\"\"\n            }\n          ]\n        },\n        \"cors-filtering.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"CORS filter on Cache-Control header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Content-Language header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Content-Type header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Expires header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Last-Modified header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Pragma header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Content-Length header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Age header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Server header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Warning header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie2 header\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Age header, header is exposed\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Server header, header is exposed\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Warning header, header is exposed\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie header, header is forbidden\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie2 header, header is forbidden\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie header, header is forbidden(credentials = include)\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"CORS filter on Set-Cookie2 header, header is forbidden(credentials = include)\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS fetch's response has cors type expected \\\"cors\\\" but got \\\"basic\\\"\"\n            }\n          ]\n        },\n        \"cors-keepalive.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"[keepalive] Same domain different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port [cors mode, server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port [cors mode, server forbid CORS]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage [cors mode, server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: Opaque filter: status is 0 expected 0 but got 200\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port [cors mode, server forbid CORS]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port [cors mode]\",\n              \"success\": false,\n              \"message\": \"assert_equals: CORS response's type is cors expected \\\"cors\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol [no-cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol [cors mode, server forbid CORS]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol [cors mode]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port GET request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port GET request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port GET request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port GET request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port GET request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port GET request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port GET request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port GET request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage GET request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage GET request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage GET request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage GET request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port GET request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port GET request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port GET request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port GET request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol GET request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol GET request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol GET request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol GET request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port POST request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port POST request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port POST request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different port POST request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port POST request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port POST request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port POST request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Same domain different protocol different port POST request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage POST request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage POST request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage POST request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain basic usage POST request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port POST request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port POST request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port POST request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different port POST request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol POST request in unload [no-cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol POST request in unload [no-cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol POST request in unload [cors mode, server forbid CORS]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"[keepalive] Cross domain different protocol POST request in unload [cors mode]; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"cors-multiple-origins.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Listing multiple origins is illegal: \\\"\\\",http://example.com,http://web-platform.test:8000\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Listing multiple origins is illegal: \\\"\\\",http://example.com,*\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Listing multiple origins is illegal: \\\"\\\",http://web-platform.test:8000,http://web-platform.test:8000\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Listing multiple origins is illegal: *,http://example.com,*\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Listing multiple origins is illegal: *,http://example.com,http://web-platform.test:8000\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Listing multiple origins is illegal: ,http://example.com,https://example2.com\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-no-preflight.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Cross domain basic usage [GET]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same domain different port [GET]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain different port [GET]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain different protocol [GET]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Same domain different protocol different port [GET]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Cross domain [POST]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [HEAD]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Accept: */*]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Accept-Language: fr]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Language: fr]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Type: application/x-www-form-urlencoded]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Type: multipart/form-data]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Type: text/plain]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Type: text/plain;charset=utf-8]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8]\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-origin.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Cross domain different subdomain [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain different subdomain [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Same domain different port [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same domain different port [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Cross domain different port [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain different port [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Cross domain different protocol [origin OK]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Cross domain different protocol [origin KO]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same domain different protocol different port [origin OK]\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Same domain different protocol different port [origin KO]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [POST] [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [POST] [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Cross domain [HEAD] [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross domain [HEAD] [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS preflight [PUT] [origin OK]\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS preflight [PUT] [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Allowed origin: \\\"\\\" [origin KO]\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-preflight-cache.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"cors-preflight-cache\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            }\n          ]\n        },\n        \"cors-preflight-not-cors-safelisted.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Loading data…\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Need CORS-preflight for accept/\\\" header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for accept/012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for accept-language/\\u0001 header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for accept-language/@ header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for authorization/basics header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for content-language/\\u0001 header\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for content-language/@ header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for content-type/text/html header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for content-type/text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for range/bytes 0- header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Need CORS-preflight for test/hi header\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            }\n          ]\n        },\n        \"cors-preflight-redirect.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirection 301 on preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 301 after preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 302 on preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 302 after preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 303 on preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 303 after preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 307 on preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 307 after preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 308 on preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirection 308 after preflight failed\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-preflight-referrer.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Referrer policy: no-referrer and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: no-referrer and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: \\\"\\\" and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: \\\"\\\" and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: no-referrer-when-downgrade and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: no-referrer-when-downgrade and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: origin and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: origin and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: origin-when-cross-origin and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: origin-when-cross-origin and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: unsafe-url and referrer: default\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Referrer policy: unsafe-url and referrer: 'myreferrer'\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            }\n          ]\n        },\n        \"cors-preflight-response-validation.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Preflight response with a bad Access-Control-Allow-Headers\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight response with a bad Access-Control-Allow-Methods\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-preflight-star.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"CORS that succeeds with credentials: false; method: GET (allowed: get); header: X-Test,1 (allowed: x-test)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: false; method: SUPER (allowed: *); header: X-Test,1 (allowed: x-test)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: false; method: OK (allowed: *); header: X-Test,1 (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: OK (allowed: *); header: X-Test,1 (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: PUT (allowed: *); header:  (allowed: )\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: GET (allowed: get); header: X-Test,1 (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: GET (allowed: *); header: X-Test,1 (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: * (allowed: *); header: *,1 (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: GET (allowed: GET); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: get (allowed: GET); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: GET (allowed: get); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: get (allowed: get); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: HEAD (allowed: HEAD); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: head (allowed: HEAD); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: HEAD (allowed: head); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: head (allowed: head); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: POST (allowed: POST); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: post (allowed: POST); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: POST (allowed: post); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: post (allowed: post); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: DELETE (allowed: DELETE); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: delete (allowed: DELETE); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: DELETE (allowed: delete); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: delete (allowed: delete); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: PUT (allowed: PUT); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: put (allowed: PUT); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: PUT (allowed: put); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: put (allowed: put); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: PATCH (allowed: PATCH); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: patch (allowed: PATCH); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that fails with credentials: true; method: PATCH (allowed: patch); header:  (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: true; method: patch (allowed: patch); header:  (allowed: *)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"CORS that fails with credentials: false; method: POST (allowed: *); header: Authorization,123 (allowed: *)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS that succeeds with credentials: false; method: POST (allowed: *); header: Authorization,123 (allowed: *, Authorization)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-preflight-status.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Preflight answered with status 200\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 201\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 202\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 203\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 204\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 205\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 206\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Preflight answered with status 300\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 301\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 302\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 303\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 304\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 305\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 306\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 307\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 308\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 400\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 401\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 402\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 403\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 404\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 405\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 501\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 502\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 503\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 504\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Preflight answered with status 505\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-preflight.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"CORS [DELETE], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [DELETE], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [PUT], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [PUT], server allows, check preflight has user agent\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 200 expected 200 but got 500\"\n            },\n            {\n              \"name\": \"CORS [PUT], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [PATCH], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [PATCH], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [patcH], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [patcH], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [NEW], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [NEW], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [chicken], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [chicken], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [GET] [x-test-header: allowed], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [GET] [x-test-header: refused], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [GET] [several headers], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [GET] [several headers], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [PUT] [several headers], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"CORS [PUT] [several headers], server refuses\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"CORS [PUT] [only safe headers], server allows\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"\\\"authorization\\\" should not be covered by the wildcard symbol\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"\\\"authorization\\\" should be covered by \\\"authorization\\\"\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-redirect-credentials.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect 301 from same origin to remote without user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from same origin to remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from same origin to remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from same origin to remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same origin with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same origin with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same origin with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to same remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to another remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to another remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 from remote to another remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from same origin to remote without user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from same origin to remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from same origin to remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from same origin to remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same origin with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same origin with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same origin with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to same remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to another remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to another remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 from remote to another remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from same origin to remote without user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from same origin to remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from same origin to remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from same origin to remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same origin with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same origin with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same origin with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to same remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to another remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to another remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 from remote to another remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from same origin to remote without user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from same origin to remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from same origin to remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from same origin to remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same origin with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same origin with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same origin with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to same remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to another remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to another remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 from remote to another remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from same origin to remote without user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from same origin to remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from same origin to remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from same origin to remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same origin with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same origin with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same origin with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to same remote with password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to another remote with user and password\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to another remote with user\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 from remote to another remote with password\",\n              \"success\": true\n            }\n          ]\n        },\n        \"cors-redirect-preflight.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect 301: same origin to cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 301: same origin to cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 301: cors to same origin (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 301: cors to same origin (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 301: cors to another cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 301: cors to another cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 302: same origin to cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: same origin to cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to same origin (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to same origin (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to another cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to another cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 303: same origin to cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: same origin to cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to same origin (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to same origin (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to another cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to another cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 307: same origin to cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: same origin to cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to same origin (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to same origin (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to another cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to another cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 308: same origin to cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: same origin to cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to same origin (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to same origin (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to another cors (preflight after redirection success case)\",\n              \"success\": false,\n              \"message\": \"assert_equals: Preflight request has been made expected \\\"1\\\" but got \\\"0\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to another cors (preflight after redirection failure case)\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"cors-redirect.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect 301: cors to same cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"Redirect 301: cors to another cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 301: same origin to cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301: cors to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to same cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: cors to another cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 302: same origin to cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302: cors to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to same cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: cors to another cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 303: same origin to cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303: cors to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to same cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: cors to another cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 307: same origin to cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307: cors to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to same cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: cors to another cors\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"Redirect 308: same origin to cors\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308: cors to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Origin is correctly set after redirect expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            }\n          ]\n        },\n        \"data-url-iframe.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetching \\\"top.txt\\\" without ACAO should be rejected.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching \\\"top.txt\\\" with CORS allowing null origin should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching data url script should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"data-url-shared-worker.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetching \\\"top.txt\\\" without ACAO should be rejected.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: SharedWorker is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching \\\"top.txt\\\" with CORS allowing null origin should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: SharedWorker is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching data url script should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: SharedWorker is not defined\\\"\"\n            }\n          ]\n        },\n        \"data-url-worker.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetching \\\"top.txt\\\" without ACAO should be rejected.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching \\\"top.txt\\\" with CORS allowing null origin should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetching data url script should be allowed.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        },\n        \"sandboxed-iframe.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"CORS with sandboxed iframe\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.addEventListener is not a function\\\"\"\n            }\n          ]\n        }\n      },\n      \"crashtests\": {\n        \"huge-fetch.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetching a huge cacheable file but not reading it should not crash\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"credentials\": {\n        \"authentication-basic.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"User-added Authorization header with include mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"User-added Authorization header with same-origin mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"User-added Authorization header with omit mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"User-added bogus Authorization header with omit mode\",\n              \"success\": true\n            }\n          ]\n        },\n        \"authentication-redirection.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"getAuthorizationHeaderValue - no redirection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"getAuthorizationHeaderValue - same origin redirection\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"getAuthorizationHeaderValue - cross origin redirection\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            }\n          ]\n        },\n        \"cookies.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Include mode: 1 cookie\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request include cookie(s) expected (string) \\\"a=1\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Include mode: 2 cookies\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request include cookie(s) expected (string) \\\"b=2; c=3\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Omit mode: discard cookies\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Omit mode: no cookie is stored\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Omit mode: no cookie is sent\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same-origin mode: 1 cookie\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request include cookie(s) expected (string) \\\"a=1\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same-origin mode: 2 cookies\",\n              \"success\": false,\n              \"message\": \"assert_equals: Request include cookie(s) expected (string) \\\"b=2; c=3\\\" but got (object) null\"\n            }\n          ]\n        }\n      },\n      \"idlharness.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"idl_test setup\",\n            \"success\": true\n          },\n          {\n            \"name\": \"idl_test validation\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Partial interface mixin WindowOrWorkerGlobalScope: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Partial interface Window: original interface defined\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Partial interface Window: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Partial interface Window[2]: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request includes Body: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response includes Body: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes GlobalEventHandlers: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes WindowEventHandlers: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes WindowOrWorkerGlobalScope: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"WorkerGlobalScope includes WindowOrWorkerGlobalScope: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes AnimationFrameProvider: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes WindowSessionStorage: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window includes WindowLocalStorage: member names are unique\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: existence and properties of interface object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface object length\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface object name\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: existence and properties of interface prototype object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: existence and properties of interface prototype object's @@unscopables property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation append(ByteString, ByteString)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation delete(ByteString)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation get(ByteString)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation getSetCookie()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation has(ByteString)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: operation set(ByteString, ByteString)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: iterable<ByteString, ByteString>\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers must be primary interface of new Headers()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Stringification of new Headers()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"append(ByteString, ByteString)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: calling append(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"delete(ByteString)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: calling delete(ByteString) on new Headers() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"get(ByteString)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: calling get(ByteString) on new Headers() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"getSetCookie()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"has(ByteString)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: new Headers() must inherit property \\\"set(ByteString, ByteString)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: existence and properties of interface object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface object length\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface object name\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: existence and properties of interface prototype object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: existence and properties of interface prototype object's @@unscopables property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute method\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute headers\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute destination\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute referrer\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute referrerPolicy\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute mode\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute credentials\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute cache\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute redirect\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute integrity\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute keepalive\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute isReloadNavigation\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute isHistoryNavigation\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute signal\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute duplex\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation clone()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute body\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: attribute bodyUsed\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation arrayBuffer()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation blob()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation bytes()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation formData()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation json()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: operation text()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request must be primary interface of new Request('about:blank')\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Stringification of new Request('about:blank')\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"method\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"url\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"headers\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"destination\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"referrer\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"referrerPolicy\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"mode\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"credentials\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"cache\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"redirect\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"integrity\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"keepalive\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"isReloadNavigation\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"isHistoryNavigation\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"signal\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"duplex\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"clone()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"body\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"bodyUsed\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"arrayBuffer()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"blob()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"bytes()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"formData()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"json()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request interface: new Request('about:blank') must inherit property \\\"text()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: existence and properties of interface object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface object length\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface object name\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: existence and properties of interface prototype object\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: existence and properties of interface prototype object's @@unscopables property\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation error()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation redirect(USVString, optional unsigned short)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation json(any, optional ResponseInit)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute redirected\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute status\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute ok\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute statusText\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute headers\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation clone()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute body\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: attribute bodyUsed\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation arrayBuffer()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation blob()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation bytes()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation formData()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation json()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: operation text()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response must be primary interface of new Response()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Stringification of new Response()\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"error()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"redirect(USVString, optional unsigned short)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: calling redirect(USVString, optional unsigned short) on new Response() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"json(any, optional ResponseInit)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: calling json(any, optional ResponseInit) on new Response() with too few arguments must throw TypeError\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"type\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"url\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"redirected\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"status\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"ok\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"statusText\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"headers\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"clone()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"body\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"bodyUsed\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"arrayBuffer()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"blob()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"bytes()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"formData()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"json()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response interface: new Response() must inherit property \\\"text()\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"FetchLaterResult interface: existence and properties of interface object\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface object length\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface object name\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface: existence and properties of interface prototype object\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface: existence and properties of interface prototype object's @@unscopables property\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"FetchLaterResult interface: attribute activated\",\n            \"success\": false,\n            \"message\": \"assert_own_property: self does not have own property \\\"FetchLaterResult\\\" expected property \\\"FetchLaterResult\\\" missing\"\n          },\n          {\n            \"name\": \"Window interface: operation fetchLater(RequestInfo, optional DeferredRequestInit)\",\n            \"success\": false,\n            \"message\": \"assert_own_property: global object missing non-static operation expected property \\\"fetchLater\\\" missing\"\n          },\n          {\n            \"name\": \"Window interface: operation fetch(RequestInfo, optional RequestInit)\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: calling operation with this = {} didn't throw TypeError Reached unreachable code\"\n          },\n          {\n            \"name\": \"Window interface: window must inherit property \\\"fetchLater(RequestInfo, optional DeferredRequestInit)\\\" with the proper type\",\n            \"success\": false,\n            \"message\": \"assert_own_property: expected property \\\"fetchLater\\\" missing\"\n          },\n          {\n            \"name\": \"Window interface: calling fetchLater(RequestInfo, optional DeferredRequestInit) on window with too few arguments must throw TypeError\",\n            \"success\": false,\n            \"message\": \"assert_own_property: expected property \\\"fetchLater\\\" missing\"\n          },\n          {\n            \"name\": \"Window interface: window must inherit property \\\"fetch(RequestInfo, optional RequestInit)\\\" with the proper type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Window interface: calling fetch(RequestInfo, optional RequestInit) on window with too few arguments must throw TypeError\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: Called with 0 arguments Reached unreachable code\"\n          }\n        ]\n      },\n      \"policies\": {\n        \"csp-blocked-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"csp-blocked.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-no-referrer-service-worker.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"referrer-no-referrer-service-worker\",\n              \"success\": false,\n              \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n            }\n          ]\n        },\n        \"referrer-no-referrer-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-no-referrer.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-origin-service-worker.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"referrer-origin-service-worker\",\n              \"success\": false,\n              \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n            }\n          ]\n        },\n        \"referrer-origin-when-cross-origin-service-worker.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"referrer-origin-when-cross-origin-service-worker\",\n              \"success\": false,\n              \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n            }\n          ]\n        },\n        \"referrer-origin-when-cross-origin-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-origin-when-cross-origin.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-origin-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-origin.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-unsafe-url-service-worker.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"referrer-unsafe-url-service-worker\",\n              \"success\": false,\n              \"message\": \"Cannot read properties of undefined (reading 'getRegistration')\"\n            }\n          ]\n        },\n        \"referrer-unsafe-url-worker.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"referrer-unsafe-url.html\": {\n          \"success\": false,\n          \"cases\": []\n        }\n      },\n      \"redirect\": {\n        \"redirect-back-to-original-origin.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"original => remote => original with mode: \\\"no-cors\\\"\",\n              \"success\": true\n            },\n            {\n              \"name\": \"original => remote => original with mode: \\\"cors\\\"\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-count.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect 301 20 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 21 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 20 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 21 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 20 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 21 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 20 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 21 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 20 times\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 21 times\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-empty-location.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"redirect response with empty Location, follow mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect response with empty Location, manual mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected \\\"opaqueredirect\\\" but got \\\"basic\\\"\"\n            }\n          ]\n        },\n        \"redirect-keepalive.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"[keepalive][new window][unload] same-origin redirect; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            },\n            {\n              \"name\": \"[keepalive][new window][unload] same-origin redirect + preflight; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            },\n            {\n              \"name\": \"[keepalive][new window][unload] cross-origin redirect; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            },\n            {\n              \"name\": \"[keepalive][new window][unload] cross-origin redirect + preflight; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            },\n            {\n              \"name\": \"[keepalive][new window][unload] redirect to file URL; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            },\n            {\n              \"name\": \"[keepalive][new window][unload] redirect to data URL; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n            }\n          ]\n        },\n        \"redirect-keepalive.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"[keepalive][iframe][load] mixed content redirect; setting up\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"redirect-location-escape.tentative.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect to escaped UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect to unescaped UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect to escaped and unescaped UTF-8\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Escaping produces double-percent\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect to invalid UTF-8\",\n              \"success\": false,\n              \"message\": \"assert_true: http://web-platform.test:8000/fetch/api/resources/top.txt?%EF%BF%BD ends with top.txt?%FF expected true got false\"\n            }\n          ]\n        },\n        \"redirect-location.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Redirect 301 in \\\"follow\\\" mode without location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"manual\\\" mode without location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"follow\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"manual\\\" mode with valid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"error\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"follow\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"manual\\\" mode with invalid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"error\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"follow\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"manual\\\" mode with data location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"Redirect 301 in \\\"error\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"follow\\\" mode without location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"manual\\\" mode without location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"follow\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"manual\\\" mode with valid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"error\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"follow\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"manual\\\" mode with invalid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"error\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"follow\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"manual\\\" mode with data location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"Redirect 302 in \\\"error\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"follow\\\" mode without location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"manual\\\" mode without location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"follow\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"manual\\\" mode with valid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"error\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"follow\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"manual\\\" mode with invalid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"error\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"follow\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"manual\\\" mode with data location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"Redirect 303 in \\\"error\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"follow\\\" mode without location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"manual\\\" mode without location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"follow\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"manual\\\" mode with valid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"error\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"follow\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"manual\\\" mode with invalid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"error\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"follow\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"manual\\\" mode with data location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"Redirect 307 in \\\"error\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"follow\\\" mode without location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"manual\\\" mode without location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"follow\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"manual\\\" mode with valid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"error\\\" mode with valid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"follow\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"manual\\\" mode with invalid location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"error\\\" mode with invalid location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"follow\\\" mode with data location\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"manual\\\" mode with data location\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"Redirect 308 in \\\"error\\\" mode with data location\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-method.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Response.redirected should be false on not-redirected responses\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 with GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 with POST\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 301 with HEAD\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 with GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 with POST\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 302 with HEAD\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 with GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 with POST\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 with HEAD\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 303 with TESTING\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 with GET\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 with POST (string body)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 with POST (blob body)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Redirect 307 with HEAD\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-mode.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"same-origin redirect 301 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 301 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 301 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"same-origin redirect 301 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"same-origin redirect 301 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 301 in follow redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 302 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 302 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 302 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"same-origin redirect 302 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"same-origin redirect 302 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 302 in follow redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 303 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 303 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 303 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"same-origin redirect 303 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"same-origin redirect 303 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 303 in follow redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 307 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 307 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 307 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"same-origin redirect 307 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"same-origin redirect 307 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 307 in follow redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 308 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 308 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 308 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"same-origin redirect 308 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"same-origin redirect 308 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"same-origin redirect 308 in follow redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 301\"\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 301 in follow redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response is opaque expected \\\"opaque\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 302\"\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 302 in follow redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response is opaque expected \\\"opaque\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 303\"\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 303 in follow redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response is opaque expected \\\"opaque\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 307\"\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 307 in follow redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response is opaque expected \\\"opaque\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in error redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in error redirect and no-cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in manual redirect and cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response's status is 0 expected 0 but got 308\"\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in manual redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in follow redirect and cors mode\",\n              \"success\": true\n            },\n            {\n              \"name\": \"cross-origin redirect 308 in follow redirect and no-cors mode\",\n              \"success\": false,\n              \"message\": \"assert_equals: Response is opaque expected \\\"opaque\\\" but got \\\"basic\\\"\"\n            },\n            {\n              \"name\": \"manual redirect with a CORS error should be rejected\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"redirect-origin.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"[GET] Redirect 301 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 301 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 301 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[GET] Redirect 301 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 301 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 301 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 301 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[POST] Redirect 301 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[GET] Redirect 302 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 302 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 302 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[GET] Redirect 302 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 302 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 302 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 302 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[POST] Redirect 302 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[GET] Redirect 303 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 303 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 303 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[GET] Redirect 303 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 303 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 303 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 303 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[POST] Redirect 303 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[GET] Redirect 307 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 307 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 307 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[GET] Redirect 307 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 307 Same origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (object) null but got (string) \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 307 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 307 Other origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 307 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[GET] Redirect 308 Same origin to same origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 308 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[GET] Redirect 308 Other origin to other origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (string) \\\"http://web-platform.test:8000\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"[GET] Redirect 308 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 308 Same origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected (object) null but got (string) \\\"http://web-platform.test:8000\\\"\"\n            },\n            {\n              \"name\": \"[POST] Redirect 308 Same origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 308 Other origin to other origin\",\n              \"success\": true\n            },\n            {\n              \"name\": \"[POST] Redirect 308 Other origin to same origin\",\n              \"success\": false,\n              \"message\": \"assert_equals: Check origin header expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n            }\n          ]\n        },\n        \"redirect-referrer-override.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Same origin redirection, no-referrer init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, strict-origin-when-cross-origin redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, no-referrer-when-downgrade init, unsafe-url redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, no-referrer-when-downgrade init, unsafe-url redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, origin-when-cross-origin init, unsafe-url redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, origin-when-cross-origin init, unsafe-url redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, strict-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, strict-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, same-origin init, unsafe-url redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, same-origin init, unsafe-url redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, strict-origin-when-cross-origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, strict-origin-when-cross-origin init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, no-referrer-when-downgrade redirect header \",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer-override.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, unsafe-url init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, unsafe-url init, unsafe-url redirect header \",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-referrer.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Same origin redirection, empty init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, same-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, origin-when-cross-origin redirect header \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, unsafe-url init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, no-referrer-when-downgrade init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, same-origin init \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected (string) \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer.any.html\\\" but got (object) null\"\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, origin-when-cross-origin init \",\n              \"success\": false,\n              \"message\": \"assert_equals: Check referrer header expected \\\"http://web-platform.test:8000/fetch/api/redirect/redirect-referrer.any.html\\\" but got \\\"http://web-platform.test:8000/\\\"\"\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, no-referrer init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, strict-origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Same origin redirection, empty redirect header, strict-origin-when-cross-origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, unsafe-url redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, no-referrer-when-downgrade redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, same-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, no-referrer redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, strict-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty init, strict-origin-when-cross-origin redirect header \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, unsafe-url init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, no-referrer-when-downgrade init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, same-origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, origin-when-cross-origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, no-referrer init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, strict-origin init \",\n              \"success\": true\n            },\n            {\n              \"name\": \"Cross origin redirection, empty redirect header, strict-origin-when-cross-origin init \",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-schemes.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"redirect-schemes\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect-schemes 1\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect-schemes 2\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect-schemes 3\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect-schemes 4\",\n              \"success\": true\n            },\n            {\n              \"name\": \"redirect-schemes 5\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-to-dataurl.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Testing data URL loading after same-origin redirection (cors mode)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing data URL loading after same-origin redirection (no-cors mode)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing data URL loading after same-origin redirection (same-origin mode)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing data URL loading after cross-origin redirection (cors mode)\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Testing data URL loading after cross-origin redirection (no-cors mode)\",\n              \"success\": true\n            }\n          ]\n        },\n        \"redirect-upload.h2.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch upload streaming should be accepted on 303\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected 200 but got 500\"\n            },\n            {\n              \"name\": \"Fetch upload streaming should fail on 301\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch upload streaming should fail on 302\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch upload streaming should fail on 307\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch upload streaming should fail on 308\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        }\n      }\n    },\n    \"compression-dictionary\": {\n      \"dictionary-clear-site-data-cache.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Clear-Site-Data with \\\"cache\\\" directive must unregister dictionary\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          }\n        ]\n      },\n      \"dictionary-clear-site-data-cookies.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Clear-Site-Data with \\\"cookies\\\" directive must unregister dictionary\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          }\n        ]\n      },\n      \"dictionary-clear-site-data-storage.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Clear-Site-Data with \\\"storage\\\" directive must not unregister dictionary\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          }\n        ]\n      },\n      \"dictionary-compressed.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Decompresion using gzip-encoded dictionary works as expected\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Decompresion using Brotli-encoded dictionary works as expected\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Decompresion using Zstandard-encoded dictionary works as expected\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"A dcb dictionary-compressed dictionary can be used as a dictionary for future requests.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"A dcz dictionary-compressed dictionary can be used as a dictionary for future requests.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"dictionary-decompression.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Decompresion using Brotli with the dictionary works as expected\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          },\n          {\n            \"name\": \"Decompresion using Zstandard with the dictionary works as expected\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          },\n          {\n            \"name\": \"Decompresion of a cross origin resource works as expected\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"dictionary-fetch-no-cors.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Fetch cross-origin no-cors request does not include Available-Dictionary header\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          }\n        ]\n      },\n      \"dictionary-fetch-with-link-element.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Browser supports link element with compression-dictionary rel.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Fetch same origin dictionary using link element\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Fetch cross origin dictionary using link element\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"dictionary-fetch-with-link-header.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Fetch same origin dictionary using link header\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"dictionary-registration.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Simple dictionary registration and unregistration\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          },\n          {\n            \"name\": \"Dictionary registration with dictionary ID\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          },\n          {\n            \"name\": \"New dictionary registration overrides the existing one\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          },\n          {\n            \"name\": \"Dictionary registration does not invalidate cache entry\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Expired dictionary is not used\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\\\" but got \\\"\\\\\\\"available-dictionary\\\\\\\" header is not available\\\"\"\n          }\n        ]\n      }\n    },\n    \"connection-pool\": {\n      \"network-partition-key.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"With credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Without credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Cross-site resources with credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Cross-site resources without credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Iframes\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Workers\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"Workers with cross-site resources\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"CSP sandbox\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"about:blank from opaque origin iframe\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      }\n    },\n    \"content-encoding\": {\n      \"br\": {\n        \"bad-br-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with arrayBuffer() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with blob() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with bytes() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with formData() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with json() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad br content with text() should reject\",\n              \"success\": true\n            }\n          ]\n        },\n        \"big-br-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"large br data should be decompressed successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"large br data should be decompressed successfully with byte stream\",\n              \"success\": true\n            }\n          ]\n        },\n        \"br-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetched br data with content type text should be decompressed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetched br data with content type octetstream should be decompressed.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"br-navigation.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Naigation to br encoded page\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        }\n      },\n      \"gzip\": {\n        \"bad-gzip-body.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching a resource with bad gzip content should still resolve\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with arrayBuffer() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with blob() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with bytes() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with formData() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with json() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad gzip content with text() should reject\",\n              \"success\": true\n            }\n          ]\n        },\n        \"big-gzip-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"large gzip data should be decompressed successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"large gzip data should be decompressed successfully with byte stream\",\n              \"success\": true\n            }\n          ]\n        },\n        \"gzip-body.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetched gzip data with content type text should be decompressed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetched gzip data with content type octetstream should be decompressed.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"gzip-navigation.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Naigation to gzip encoded page\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        }\n      },\n      \"zstd\": {\n        \"bad-zstd-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetching a resource with bad zstd content should still resolve\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with arrayBuffer() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with blob() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with bytes() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with formData() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with json() should reject\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Consuming the body of a resource with bad zstd content with text() should reject\",\n              \"success\": true\n            }\n          ]\n        },\n        \"big-window-zstd-body.tentative.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Consuming the body of a resource with too large of a zstd window size should reject\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n            }\n          ]\n        },\n        \"big-zstd-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"large zstd data should be decompressed successfully\",\n              \"success\": true\n            },\n            {\n              \"name\": \"large zstd data should be decompressed successfully with byte stream\",\n              \"success\": true\n            }\n          ]\n        },\n        \"zstd-body.https.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetched zstd data with content type text should be decompressed.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetched zstd data with content type octetstream should be decompressed.\",\n              \"success\": true\n            }\n          ]\n        },\n        \"zstd-navigation.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Naigation to zstd encoded page\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        }\n      }\n    },\n    \"content-length\": {\n      \"api-and-duplicate-headers.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"fetch() and duplicate Content-Length/Content-Type headers\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"XMLHttpRequest and duplicate Content-Length/Content-Type headers\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"content-length.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"parsing.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Loading JSON…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42\\\". Expected: 42.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42,42\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42\\\\r\\\\nContent-Length: 42\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42\\\\r\\\\nContent-Length: 42,42\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30\\\". Expected: 30.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,30\\\". Expected: 30.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30\\\\r\\\\nContent-Length: 30\\\". Expected: 30.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30\\\\r\\\\nContent-Length: 30,30\\\". Expected: 30.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,30\\\\r\\\\nContent-Length: 30,30\\\". Expected: 30.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,30,  30  \\\\r\\\\nContent-Length: 30  \\\". Expected: 30.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,42\\\\r\\\\nContent-Length: 30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,42\\\\r\\\\nContent-Length: 30,42\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42,30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,42\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42\\\\r\\\\nContent-Length: 30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30\\\\r\\\\nContent-Length: 42\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30,\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: ,30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30\\\\r\\\\nContent-Length: \\\\t\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: \\\\r\\\\nContent-Length: 30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: aaaah\\\\r\\\\nContent-Length: nah\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: aaaah, nah\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: aaaah\\\\r\\\\nContent-Length: aaaah\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: aaaah, aaaah\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: aaaah\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 42s\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 30s\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: -1\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 0x20\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 030\\\". Expected: 30.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 030\\\\r\\\\nContent-Length: 30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: 030, 30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: \\\\\\\"30\\\\\\\"\\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length:30\\\\r\\\\nContent-Length:,\\\\r\\\\nContent-Length:30\\\". Expected: network error.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Input: \\\"Content-Length: \\\". Expected: 42.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"too-long.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Content-Length header value of network response exceeds response body\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"content-type\": {\n      \"multipart-malformed.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Invalid form data should not crash the browser\",\n            \"success\": true\n          }\n        ]\n      },\n      \"multipart.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"multipart\",\n            \"success\": true\n          }\n        ]\n      },\n      \"response.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Loading JSON…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type:  text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type:  text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type:  text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type:  text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type:  text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type:  text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain \",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain \",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain \",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain \",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain;charset=gbk text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain;charset=gbk text/html;charset=windows-1254\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain;charset=gbk text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain;charset=gbk text/plain;charset=windows-1252\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;charset=gbk text/html;x=\\\",text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain;charset=gbk;x=foo text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;charset=gbk text/plain text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain */*\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain */*\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html */*\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html */*\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html */*\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: */* text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: */* text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: */* text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: */* text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: */* text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: */* text/html\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/plain */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html */*;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html */*;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html */*;charset=gbk\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;x=\\\\\\\", text/plain\\\\\\\"\\\" but got \\\"text/html;x=\\\\\\\",text/plain\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;x=\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;\\\" \\\\\\\" text/plain \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"<iframe>: separate response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<iframe>: combined response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetch(): separate response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"fetch(): combined response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Request: combined response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Response: combined response Content-Type: text/html;\\\" \\\" text/plain\",\n            \"success\": true\n          }\n        ]\n      },\n      \"script.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Loading JSON…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"separate text/javascript;charset=windows-1252\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;\\\";charset=windows-1252\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript\\f\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate \\\"text/javascript\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/ javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text /javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate x/x text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined x/x text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate x/x;charset=windows-1252 text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined x/x;charset=windows-1252 text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript; charset=windows-1252 text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript; charset=windows-1252 text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;\\\" x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript;\\\" x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript error\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript error\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;charset=windows-1252 x/x text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript;charset=windows-1252 x/x text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;charset=windows-1252 error text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript;charset=windows-1252 error text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;charset=windows-1252  text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript;charset=windows-1252  text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate text/javascript;charset=windows-1252;\\\" \\\\\\\" x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined text/javascript;charset=windows-1252;\\\" \\\\\\\" x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"separate x/x;\\\" x/y;\\\\\\\" text/javascript;charset=windows-1252;\\\" text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"combined x/x;\\\" x/y;\\\\\\\" text/javascript;charset=windows-1252;\\\" text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      }\n    },\n    \"corb\": {\n      \"img-mime-types-coverage.tentative.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'null'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: ''.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'x'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'x/x'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'image/gif'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'image/png'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'image/png;blah'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'image/svg+xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/javascript'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/jsonp'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/dash+xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'image/gif;HI=THERE'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/octet-stream'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/x-www-form-urlencoded'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'text/x-json'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'text/json+blah'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/json+blah'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'text/xml+blah'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/xml+blah'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'application/blahjson'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should allow the response if Content-Type is: 'text/blahxml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/html'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/json'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'application/json'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'application/xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'application/blah+json'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/blah+json'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'application/blah+xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/blah+xml'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'TEXT/HTML'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'TEXT/JSON'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'TEXT/BLAH+JSON'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'APPLICATION/BLAH+XML'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/json;does=it;matter'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB should block the response if Content-Type is: 'text/HTML;NO=it;does=NOT'.  \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"response_block.tentative.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"ORB: Expect error response from <script> fetch.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ORB: Expect error response from fetch().\",\n            \"success\": true\n          }\n        ]\n      },\n      \"script-html-correctly-labeled.tentative.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"script-html-js-polyglot.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"CORB cannot block polyglot HTML/JS: html-js-polyglot.js\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"CORB cannot block polyglot HTML/JS: html-js-polyglot2.js\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"script-html-via-cross-origin-blob-url.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"script-html-via-cross-origin-blob-url\",\n            \"success\": false,\n            \"message\": \"addEventListener is not defined\"\n          }\n        ]\n      },\n      \"script-js-mislabeled-as-html-nosniff.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"script-js-mislabeled-as-html-nosniff\",\n            \"success\": true\n          }\n        ]\n      },\n      \"script-js-mislabeled-as-html.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"script-resource-with-json-parser-breaker.tentative.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"script-resource-with-nonsniffable-types.tentative.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"style-css-mislabeled-as-html-nosniff.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"style-css-mislabeled-as-html-nosniff\",\n            \"success\": false,\n            \"message\": \"getComputedStyle is not defined\"\n          }\n        ]\n      },\n      \"style-css-mislabeled-as-html.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"style-css-mislabeled-as-html\",\n            \"success\": false,\n            \"message\": \"getComputedStyle is not defined\"\n          }\n        ]\n      },\n      \"style-css-with-json-parser-breaker.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"style-css-with-json-parser-breaker\",\n            \"success\": false,\n            \"message\": \"getComputedStyle is not defined\"\n          }\n        ]\n      },\n      \"style-html-correctly-labeled.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"style-html-correctly-labeled\",\n            \"success\": false,\n            \"message\": \"getComputedStyle is not defined\"\n          }\n        ]\n      }\n    },\n    \"cross-origin-resource-policy\": {\n      \"fetch-in-iframe.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-origin fetch in a cross origin iframe load succeeds if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"fetch.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n          },\n          {\n            \"name\": \"Cross-scheme (HTTP to HTTPS) no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a cross-origin redirection.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Should have rejected: undefined Reached unreachable code\"\n          }\n        ]\n      },\n      \"fetch.https.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a cross-origin redirection.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"iframe-loads.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Load an iframe that has Cross-Origin-Resource-Policy header\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"[object Response]\\\"\"\n          }\n        ]\n      },\n      \"image-loads.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"scheme-restriction.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Cross-Origin-Resource-Policy: same-site blocks retrieving HTTPS from HTTP\",\n            \"success\": true\n          }\n        ]\n      },\n      \"scheme-restriction.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Cross-Origin-Resource-Policy does not block Mixed Content <img>\",\n            \"success\": false,\n            \"message\": \"Image is not defined\"\n          }\n        ]\n      },\n      \"script-loads.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"syntax.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: same\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: same, same-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: SAME-ORIGIN\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: Same-Origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: same-origin, <>\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: same-origin, same-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing Cross-Origin-Resource-Policy: https://www.example.com\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"data-urls\": {\n      \"base64.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Setup.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\" abcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd \\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\" abcd===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd=== \\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd ===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"𐀀\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde==\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde===\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcde=====\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"=a\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"=a=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a=b\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"a=b=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab=c\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab=c=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc=d\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abc=d=\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\vcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab　cd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab、cd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\tcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\ncd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\fcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\rcd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab cd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab cd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\t\\\\n\\\\f\\\\r cd\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\" \\\\t\\\\n\\\\f\\\\r ab\\\\t\\\\n\\\\f\\\\r cd\\\\t\\\\n\\\\f\\\\r \\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"ab\\\\t\\\\n\\\\f\\\\r =\\\\t\\\\n\\\\f\\\\r =\\\\t\\\\n\\\\f\\\\r \\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"/A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"//A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"///A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"////A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"/\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"A/\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"AA/\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"AAAA/\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"AAA/\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"\\\\0nonsense\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"abcd\\\\0nonsense\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"YQ\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"YR\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"~~\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"..\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"--\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL base64 handling: \\\"__\\\"\",\n            \"success\": true\n          }\n        ]\n      },\n      \"navigate.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Nothing fancy\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"base64\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"base64 with code points that differ from base64url\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"ASCII whitespace in the input is removed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"base64 with incorrect padding\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"base64url is not supported\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          },\n          {\n            \"name\": \"Vertical tab in the input leads to an error\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: self.addEventListener is not a function\\\"\"\n          }\n        ]\n      },\n      \"processing.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Setup.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data://test/,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data://test:test/,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/html\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/html    ;charset=x   \\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:,\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:,X#X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain ,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain%20,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain\\\\f,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain%0C,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;x=x;charset=x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;x=x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;charset=windows-1252,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;Charset=UTF-8,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;charset=windows-1252,áñçə💩\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;charset=UTF-8,áñçə💩\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:image/gif,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:IMAGE/gif,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:IMAGE/gif;hi=x,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:IMAGE/gif;CHARSET=x,%C2%B1\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data: ,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:%20,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:\\\\f,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:%1F,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:\\\\0,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:%00,%FF\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/html  ,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text / html,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:†,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:†/†,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:X,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:image/png,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:application/javascript,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:application/xml,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/javascript,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:unknown/unknown,X X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;a=\\\\\\\",\\\\\\\",X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:text/plain;a=%2C,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64;base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x/x;base64;base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x/x;base64;charset=x,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x/x;base64;charset=x;base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x/x;base64;base64x,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64,W%20A\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64,W%0CA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x;base64x,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x;base64;x,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:x;base64=x,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:; base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;  base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:  ;charset=x   ;  base64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64;,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64 ,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base64   ,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;base 64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;BASe64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;%62ase64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:%3Bbase64,WA\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset=x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:; charset=x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset =x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset= x,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset=,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset,X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;charset=\\\\\\\"x\\\\\\\",X\\\"\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"data:;CHARSET=\\\\\\\"X\\\\\\\",X\\\"\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"fetch-later\": {\n      \"activate-after.tentative.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"fetchLater() sends out based on activateAfter.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() sends out based on activateAfter, even if document is in BFCache.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"basic.tentative.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"fetchLater() cannot be called without request.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater()\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() with same-origin (https) URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with http://localhost URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with https://localhost URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with http://127.0.0.1 URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with https://127.0.0.1 URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with http://[::1] URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with https://[::1] URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() with https://example.com URL does not throw.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() throws SecurityError on non-trustworthy http URL.\",\n            \"success\": false,\n            \"message\": \"assert_throws_dom: should throw SecurityError for insecure http url http://example.com function \\\"() => fetchLater(httpUrl)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a DOMException SecurityError: property \\\"code\\\" is equal to undefined, expected 18\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on file:// scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('file://tmp')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on ftp:// scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('ftp://example.com')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on ssh:// scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('ssh://example.com')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on wss:// scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('wss://example.com')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on about: scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('about:blank')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on javascript: scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater(`javascript:alert('');`)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on data: scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('data:text/plain,Hello')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError on blob: scheme.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('blob:https://example.com/some-uuid')\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater() throws RangeError on negative activateAfter.\",\n            \"success\": false,\n            \"message\": \"assert_throws_js: function \\\"() => fetchLater('https://www.google.com', {activateAfter: -1})\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function RangeError() { [native code] }\\\" (\\\"RangeError\\\")\"\n          },\n          {\n            \"name\": \"fetchLater()'s return tells the deferred request is not yet sent.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() throws TypeError when mutating its returned state.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          },\n          {\n            \"name\": \"fetchLater() throws AbortError when its initial abort signal is aborted.\",\n            \"success\": false,\n            \"message\": \"assert_throws_dom: function \\\"() => fetchLater('/', {signal: controller.signal})\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a DOMException AbortError: property \\\"code\\\" is equal to undefined, expected 20\"\n          },\n          {\n            \"name\": \"fetchLater() does not throw error when it is aborted before sending.\",\n            \"success\": false,\n            \"message\": \"fetchLater is not defined\"\n          }\n        ]\n      },\n      \"headers\": {\n        \"header-referrer-no-referrer-when-downgrade.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-no-referrer.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header \",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-origin-when-cross-origin.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"Test referer header https://www1.web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-origin.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://www1.web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-same-origin.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header \",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"Test referer header https://www1.web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-strict-origin-when-cross-origin.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://www1.web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-strict-origin.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"header-referrer-unsafe-url.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Test referer header https://web-platform.test:8443\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        }\n      },\n      \"iframe.tentative.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"A blank iframe can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"new-window.tentative.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"A blank window[target=''][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A same-origin window[target=''][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A cross-origin window[target=''][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A blank window[target=''][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A same-origin window[target=''][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A cross-origin window[target=''][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A blank window[target='_blank'][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A same-origin window[target='_blank'][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A cross-origin window[target='_blank'][features=''] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A blank window[target='_blank'][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A same-origin window[target='_blank'][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"A cross-origin window[target='_blank'][features='popup'] can trigger fetchLater.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"non-secure.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"fetchLater() is not supported in non-secure context.\",\n            \"success\": true\n          }\n        ]\n      },\n      \"permissions-policy\": {\n        \"deferred-fetch-allowed-by-permissions-policy-attribute-redirect.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Permissions policy allow=\\\"deferred-fetch\\\" allows fetchLater() from a redirected same-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Permissions policy allow=\\\"deferred-fetch\\\" disallows fetchLater() from a redirected cross-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"deferred-fetch-allowed-by-permissions-policy-attribute.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Permissions policy \\\"deferred-fetch\\\" can be enabled in the same-origin iframe using allow=\\\"deferred-fetch\\\" attribute.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Permissions policy \\\"deferred-fetch\\\" can be enabled in the cross-origin iframe using allow=\\\"deferred-fetch\\\" attribute.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"deferred-fetch-allowed-by-permissions-policy.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Permissions policy header: \\\"deferred-fetch=*\\\" allows fetchLater() in the top-level document.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"Permissions policy header: \\\"deferred-fetch=*\\\" allows fetchLater() in the same-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Permissions policy header: \\\"deferred-fetch=*\\\" allows fetchLater() in the cross-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Permissions policy header: \\\"deferred-fetch=*\\\" allow=\\\"deferred-fetch\\\" allows fetchLater() in the cross-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"deferred-fetch-default-permissions-policy.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Default \\\"deferred-fetch\\\" permissions policy [\\\"self\\\"] allows fetchLater() in the top-level document.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"Default \\\"deferred-fetch\\\" permissions policy [\\\"self\\\"] allows fetchLater() in the same-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Default \\\"deferred-fetch-minimal\\\" permissions policy [\\\"*\\\"] allows fetchLater() in the cross-origin iframe.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"deferred-fetch-supported-by-permissions-policy.tentative.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"document.featurePolicy.features should advertise deferred-fetch.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        }\n      },\n      \"policies\": {\n        \"csp-allowed.tentative.https.window.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"csp-blocked.tentative.https.window.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"csp-redirect-to-blocked.tentative.https.window.html\": {\n          \"success\": false,\n          \"cases\": []\n        }\n      },\n      \"quota\": {\n        \"accumulated-oversized-payload.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"The 2nd fetchLater(same-origin) call in the top-level document is not allowed to exceed per-origin quota for its POST body of String.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"cross-origin-iframe\": {\n          \"accumulated-oversized-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"The 2nd fetchLater(same-origin) call in a default cross-origin child iframe has its owned per-origin quota for a request POST body of String.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: fetchLater is not defined\\\"\"\n              }\n            ]\n          },\n          \"empty-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of ArrayBuffer in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts a non-empty POST request body of FormData in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of URLSearchParams in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of Blob in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of File in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"max-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() accepts max payload in a parent-frame-origin POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              },\n              {\n                \"name\": \"fetchLater() rejects max+1 payload in a parent-frame-origin POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              },\n              {\n                \"name\": \"fetchLater() accepts max payload in a self-frame-origin POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              },\n              {\n                \"name\": \"fetchLater() rejects max+1 payload in a self-frame-origin POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"multiple-iframes.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() request quota are delegated to cross-origin iframes and not shared, even if they are same origin.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"oversized-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of ArrayBuffer in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of FormData in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of URLSearchParams in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of Blob in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=8193] exceeding per-origin quota in a POST request body of File in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"small-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of String in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of ArrayBuffer in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of FormData in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of URLSearchParams in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of Blob in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of File in a default cross-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          }\n        },\n        \"empty-payload.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetchLater() does not accept an empty POST request body of String.\",\n              \"success\": false,\n              \"message\": \"assert_throws_js: function \\\"() => fetchLater('/', requestInit)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept an empty POST request body of ArrayBuffer.\",\n              \"success\": false,\n              \"message\": \"assert_throws_js: function \\\"() => fetchLater('/', requestInit)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n            },\n            {\n              \"name\": \"fetchLater() accepts a non-empty POST request body of FormData.\",\n              \"success\": true\n            },\n            {\n              \"name\": \"fetchLater() does not accept an empty POST request body of URLSearchParams.\",\n              \"success\": false,\n              \"message\": \"assert_throws_js: function \\\"() => fetchLater('/', requestInit)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept an empty POST request body of Blob.\",\n              \"success\": false,\n              \"message\": \"assert_throws_js: function \\\"() => fetchLater('/', requestInit)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept an empty POST request body of File.\",\n              \"success\": false,\n              \"message\": \"assert_throws_js: function \\\"() => fetchLater('/', requestInit)\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" (\\\"ReferenceError\\\") expected instance of function \\\"function TypeError() { [native code] }\\\" (\\\"TypeError\\\")\"\n            },\n            {\n              \"name\": \"fetchLater() accept a GET request.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accept a DELETE request.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accept a PUT request.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"max-payload.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetchLater() accepts max payload in a POST request body of String.\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: fetchLater is not defined\\\"\"\n            },\n            {\n              \"name\": \"fetchLater() rejects max+1 payload in a POST request body of String.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n    fetchLater(requestUrl, {\\n          activateAfter: 0,\\n          method: 'POST',\\n          body: generatePayload(\\n              getRemainingQuota(QUOTA_PER_ORIGIN, requestUrl, headers) + 1,\\n              dataType),\\n        });\\n  }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            }\n          ]\n        },\n        \"multiple-origins.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of String.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of ArrayBuffer.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of FormData.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of URLSearchParams.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of Blob.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() has per-request-origin quota for its POST body of File.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        },\n        \"oversized-payload.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of String.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of ArrayBuffer.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of FormData.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of URLSearchParams.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of Blob.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            },\n            {\n              \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of File.\",\n              \"success\": false,\n              \"message\": \"assert_throws_quotaexceedederror: function \\\"() => {\\n      fetchLater('/', {\\n        activateAfter: 0,\\n        method: 'POST',\\n        body: makeBeaconData(\\n            generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType),\\n      });\\n    }\\\" threw object \\\"ReferenceError: fetchLater is not defined\\\" that is not a correct QuotaExceededError: property \\\"code\\\" is equal to undefined, expected 22\"\n            }\n          ]\n        },\n        \"same-origin-iframe\": {\n          \"accumulated-oversized-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"The 2nd fetchLater(same-origin) call in a same-origin child iframe is not allowed to exceed per-origin quota for its POST body of String.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: fetchLater is not defined\\\"\"\n              }\n            ]\n          },\n          \"empty-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of String in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of ArrayBuffer in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts a non-empty POST request body of FormData in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of URLSearchParams in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of Blob in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept empty POST request body of File in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"max-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() accepts max payload in a POST request body of String in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              },\n              {\n                \"name\": \"fetchLater() rejects max+1 payload in a POST request body of String in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"multiple-iframes.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() request quota are shared by same-origin iframes and root.\",\n                \"success\": false,\n                \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n              }\n            ]\n          },\n          \"oversized-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of String in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of ArrayBuffer in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of FormData in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of URLSearchParams in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of Blob in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() does not accept payload[size=65537] exceeding per-origin quota in a POST request body of File in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"small-payload.tentative.https.window.html\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of String in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of ArrayBuffer in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of FormData in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of URLSearchParams in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of Blob in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"fetchLater() accepts payload[size=20] in a POST request body of File in same-origin iframe.\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          }\n        },\n        \"small-payload.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of String.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of ArrayBuffer.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of FormData.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of URLSearchParams.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of Blob.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            },\n            {\n              \"name\": \"fetchLater() accepts small payload in a POST request body of File.\",\n              \"success\": false,\n              \"message\": \"fetchLater is not defined\"\n            }\n          ]\n        }\n      },\n      \"send-on-deactivate-with-background-sync.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"send-on-deactivate.tentative.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"fetchLater() sends on page entering BFCache if BackgroundSync is off.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"Call fetchLater() when BFCached with activateAfter=0 sends immediately.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"fetchLater() sends on navigating away a page w/o BFCache.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"fetchLater() does not send aborted request on navigating away a page w/o BFCache.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"fetchLater() with activateAfter=1m sends on page entering BFCache if BackgroundSync is off.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"send-on-discard\": {\n        \"not-send-after-abort.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A discarded document does not send an already aborted fetchLater request.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"send-multiple-with-activate-after.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A discarded document sends all its fetchLater requests, no matter how much their activateAfter timeout remain.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"send-multiple.tentative.https.window.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"A discarded document sends all its fetchLater requests.\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        }\n      }\n    },\n    \"h1-parsing\": {\n      \"lone-cr.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1\\r200 OK\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200\\rOK\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\n\\rHeader: Value\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader\\r: Value\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader:\\r Value\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader: Value\\r\\n\\nBody)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader: Value\\r\\r\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader: Value\\rHeader2: Value2\\n\\nBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader: Value\\n\\rBODY)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Parsing response with a lone CR before message-body (HTTP/1.1 200 OK\\nHeader: Value\\n\\r)\",\n            \"success\": true\n          }\n        ]\n      },\n      \"resources-with-0x00-in-header.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Expect network error for script with 0x00 in a header\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Expect network error for frame navigation to resource with 0x00 in a header\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Expect network error for image with 0x00 in a header\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Expect network error for fetch with 0x00 in a header\",\n            \"success\": true\n          }\n        ]\n      },\n      \"status-code.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP/1.1  (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 BLAH (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 0 OK \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 1 OK \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 99 NOT OK \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 077 77 \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 099 HELLO \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 200 \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 999 DOES IT MATTER \",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"HTTP/1.1 1000 BOO (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 0200 BOO (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 65736 NOT 200 OR SOME SUCH (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 131072 HI (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 -200 TEST (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 0xA (network error)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP/1.1 C8 (network error)\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"http-cache\": {\n      \"304-update.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache updates returned headers from a Last-Modified 304\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          },\n          {\n            \"name\": \"HTTP cache updates stored headers from a Last-Modified 304\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          },\n          {\n            \"name\": \"HTTP cache updates returned headers from a ETag 304\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          },\n          {\n            \"name\": \"HTTP cache updates stored headers from a ETag 304\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          },\n          {\n            \"name\": \"Content-* header\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          }\n        ]\n      },\n      \"cache-mode.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Fetch sends Cache-Control: max-age=0 when cache mode is no-cache\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Fetch doesn't touch Cache-Control when cache mode is no-cache and Cache-Control is already present\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Fetch sends Cache-Control: no-cache and Pragma: no-cache when cache mode is no-store\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Fetch doesn't touch Cache-Control when cache mode is no-store and Cache-Control is already present\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Fetch doesn't touch Pragma when cache mode is no-store and Pragma is already present\",\n            \"success\": true\n          }\n        ]\n      },\n      \"cc-request.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache doesn't use aged but fresh response when request contains Cache-Control: max-age=0\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't use aged but fresh response when request contains Cache-Control: max-age=1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't use fresh response with Age header when request contains Cache-Control: max-age that is greater than remaining freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does use aged stale response when request contains Cache-Control: max-stale that permits its use\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does reuse stale response with Age header when request contains Cache-Control: max-stale that permits its use\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't reuse fresh response when request contains Cache-Control: min-fresh that wants it fresher\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't reuse fresh response with Age header when request contains Cache-Control: min-fresh that wants it fresher\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't reuse fresh response when request contains Cache-Control: no-cache\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache validates fresh response with Last-Modified when request contains Cache-Control: no-cache\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache validates fresh response with ETag when request contains Cache-Control: no-cache\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't reuse fresh response when request contains Cache-Control: no-store\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache generates 504 status code when nothing is in cache and request contains Cache-Control: only-if-cached\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 1 status is 200, not 504 expected 504 but got 200\"\n          }\n        ]\n      },\n      \"credentials.tentative.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"same-origin: 2xAnonymous, 2xCredentialled, 1xAnonymous\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 header Server-Request-Count is \\\"2\\\", not \\\"1\\\" expected \\\"1\\\" but got \\\"2\\\"\"\n          },\n          {\n            \"name\": \"same-origin: 2xCredentialled, 2xAnonymous, 1xCredentialled\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 header Server-Request-Count is \\\"2\\\", not \\\"1\\\" expected \\\"1\\\" but got \\\"2\\\"\"\n          }\n        ]\n      },\n      \"freshness.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache reuses a response with a future Expires\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with a past Expires\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with a present Expires\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with an invalid Expires\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with an invalid Expires with Last-Modified now\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with an invalid Expires with past Last-Modified\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache reuses a response with positive Cache-Control: max-age\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with Cache-Control: max-age=0\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache reuses a response with positive Cache-Control: max-age and a past Expires\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a response with positive Cache-Control: max-age and an invalid Expires\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response with Cache-Control: max-age=0 and a future Expires\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not prefer Cache-Control: s-maxage over Cache-Control: max-age\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a response when the Age header is greater than its freshness lifetime\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not store a response with Cache-Control: no-store\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not store a response with Cache-Control: no-store, even with max-age and Expires\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache stores a response with Cache-Control: no-cache, but revalidates upon use\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          },\n          {\n            \"name\": \"HTTP cache stores a response with Cache-Control: no-cache, but revalidates upon use, even with max-age and Expires\",\n            \"success\": false,\n            \"message\": \"assert_equals: Response 2 status is 999, not 200 expected 200 but got 999\"\n          }\n        ]\n      },\n      \"heuristic.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache reuses an unknown response with Last-Modified based upon heuristic freshness when Cache-Control: public is present\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does not reuse an unknown response with Last-Modified based upon heuristic freshness when Cache-Control: public is not present\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not reuse a redirected response with no max-age and no Last-Modified header\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache reuses a 200 OK response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 203 Non-Authoritative Information response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 204 No Content response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 404 Not Found response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 405 Method Not Allowed response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 410 Gone response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 414 URI Too Long response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses a 501 Not Implemented response with Last-Modified based upon heuristic freshness\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache does not use a 201 Created response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not use a 202 Accepted response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not use a 403 Forbidden response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not use a 502 Bad Gateway response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not use a 503 Service Unavailable response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not use a 504 Gateway Timeout response with Last-Modified based upon heuristic freshness\",\n            \"success\": true\n          }\n        ]\n      },\n      \"invalidate.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache invalidates after a successful response from a POST\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not invalidate after a failed response from an unsafe request\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 3 does not come from cache expected a number less than 3 but got 3\"\n          },\n          {\n            \"name\": \"HTTP cache invalidates after a successful response from a PUT\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates after a successful response from a DELETE\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates after a successful response from an unknown method\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Location URL after a successful response from a POST\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not invalidate Location URL after a failed response from an unsafe request\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 3 does not come from cache expected a number less than 3 but got 3\"\n          },\n          {\n            \"name\": \"HTTP cache invalidates Location URL after a successful response from a PUT\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Location URL after a successful response from a DELETE\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Location URL after a successful response from an unknown method\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Content-Location URL after a successful response from a POST\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache does not invalidate Content-Location URL after a failed response from an unsafe request\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 3 does not come from cache expected a number less than 3 but got 3\"\n          },\n          {\n            \"name\": \"HTTP cache invalidates Content-Location URL after a successful response from a PUT\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Content-Location URL after a successful response from a DELETE\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache invalidates Content-Location URL after a successful response from an unknown method\",\n            \"success\": true\n          }\n        ]\n      },\n      \"no-vary-search.tentative.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"When params is set to true, URL differs only by their parameters (other than `dispatch` and `uuid`) should not be cached as different entries.\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"Ground truth: When key-order is not set, URLs should be compared in an order-sensitive way.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"When key-order is set , URLs should be compared in an order-insensitive way. Matched cases:\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"When key-order is set , URLs should be compared in an order-insensitive way. Not matched cases\",\n            \"success\": true\n          }\n        ]\n      },\n      \"partial.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache stores partial content and reuses it\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores complete response and serves smaller ranges from it (byte-range-spec)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores complete response and serves smaller ranges from it (absent last-byte-pos)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores complete response and serves smaller ranges from it (suffix-byte-range-spec)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores complete response and serves smaller ranges from it with only-if-cached\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores partial response and serves smaller ranges from it (byte-range-spec)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores partial response and serves smaller ranges from it (absent last-byte-pos)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores partial response and serves smaller ranges from it (suffix-byte-range-spec)\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache stores partial content and completes it\",\n            \"success\": false,\n            \"message\": \"assert_equals: request 2 header range value is \\\"undefined\\\", not \\\"bytes=5-\\\" expected (string) \\\"bytes=5-\\\" but got (undefined) undefined\"\n          }\n        ]\n      },\n      \"post-patch.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache uses content after PATCH request with response containing Content-Location and cache-allowing header\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache uses content after POST request with response containing Content-Location and cache-allowing header\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          }\n        ]\n      },\n      \"pragma-no-cache-with-cache-control.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Response with Cache-Control: max-age=2592000, public and Pragma: no-cache should be cached\",\n            \"success\": false,\n            \"message\": \"assert_equals: Responses should be identical, indicating caching expected \\\"Timestamp: 1768756436.8170867\\\" but got \\\"Timestamp: 1768756436.8148103\\\"\"\n          }\n        ]\n      },\n      \"split-cache.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache is shared between same-site top-level frames\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"HTTP cache is not shared between cross-site top-level frames\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"HTTP cache is shared between same-site frames with same-site top-level frames\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          },\n          {\n            \"name\": \"HTTP cache is not shared between same-site frames with cross-site top-level frames\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n          }\n        ]\n      },\n      \"status.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 200 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 200 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 203 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 203 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 204 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 204 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 299 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 299 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 400 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 400 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 404 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 404 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 410 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 410 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 499 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 499 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 500 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 500 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 502 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 502 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 503 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 503 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 504 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 504 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache goes to the network if it has a stale 599 response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache avoids going to the network if it has a fresh 599 response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          }\n        ]\n      },\n      \"vary.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP cache reuses Vary response when request matches\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't use Vary response when request doesn't match\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't use Vary response when request omits variant header\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't invalidate existing Vary response\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 3 does not come from cache expected a number less than 3 but got 3\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't pay attention to headers not listed in Vary\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache reuses two-way Vary response when request matches\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't use two-way Vary response when request doesn't match\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't use two-way Vary response when request omits variant header\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache reuses three-way Vary response when request matches\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't use three-way Vary response when request doesn't match\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache doesn't use three-way Vary response when request doesn't match, regardless of header order\",\n            \"success\": true\n          },\n          {\n            \"name\": \"HTTP cache uses three-way Vary response when both request and the original request omited a variant header\",\n            \"success\": false,\n            \"message\": \"assert_less_than: Response 2 does not come from cache expected a number less than 2 but got 2\"\n          },\n          {\n            \"name\": \"HTTP cache doesn't use Vary response with a field value of '*'\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"images\": {\n      \"canvas-remote-read-remote-image-redirect.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"local-network-access\": {\n      \"fetch.tentative.https.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-loopback\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.https.window.html?include=from-loopback\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.https.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.window.html?include=from-loopback\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"navigate.tentative.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"metadata\": {\n      \"audio-worklet.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"The fetch metadata for audio worklet\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: AudioContext is not defined\\\"\"\n          }\n        ]\n      },\n      \"embed.https.sub.tentative.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Wrapper: same-origin embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Wrapper: Navigate to same-origin embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Wrapper: same-site embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Wrapper: Navigate to same-site embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Wrapper: cross-site embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Wrapper: Navigate to cross-site embed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"fetch-preflight.https.sub.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-site fetch with preflight\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Cross-site fetch with preflight\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"fetch.https.sub.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-origin fetch\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Same-site fetch\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Cross-site fetch\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Same-origin mode\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"CORS mode\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"no-CORS mode\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"generated\": {\n        \"audioworklet.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"css-font-face.https.sub.tentative.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"css-font-face.sub.tentative.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"css-images.https.sub.tentative.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"css-images.sub.tentative.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-a.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-a.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"element-area.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-area.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - no attributes\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"element-audio.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-audio.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-embed.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-embed.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-frame.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-frame.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-iframe.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-iframe.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-img-environment-change.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-img-environment-change.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-img.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - src - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - src - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - srcset - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - src - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - srcset - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - src - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - srcset - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - src - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - srcset - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-img.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - src - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - src - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - srcset - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - src - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - srcset - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - src - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - srcset - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - src - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - srcset - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - src - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - srcset - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - src - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - srcset - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - src - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - srcset - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - src - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - srcset - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - src - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - srcset - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - src - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - srcset - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - src - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - srcset - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-input-image.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-input-image.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-link-icon.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-link-icon.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-link-prefetch.https.optional.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-link-prefetch.optional.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"element-meta-refresh.https.optional.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"element-meta-refresh.optional.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"element-picture.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[src] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - source[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[src] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - source[srcset] - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[src] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[srcset] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - source[srcset] - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[src] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[srcset] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - source[srcset] - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-picture.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - img[src] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[src] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[src] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[src] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[src] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[src] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[src] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[src] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[src] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[src] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[src] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[src] - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - img[srcset] - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - source[srcset] - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-script.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same origin, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-script.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, attributes: type=module\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-video-poster.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-video-poster.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-video.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"element-video.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"fetch.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site, init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - init: mode=cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - init: mode=no-cors\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - init: mode=same-origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site, init: mode=no-cors, credentials=include\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site, init: mode=no-cors, credentials=include\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            }\n          ]\n        },\n        \"fetch.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no init\",\n              \"success\": false,\n              \"message\": \"assert_not_own_property: unexpected property \\\"sec-fetch-mode\\\" is found on object\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination, no init\",\n              \"success\": false,\n              \"message\": \"assert_not_own_property: unexpected property \\\"sec-fetch-mode\\\" is found on object\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no init\",\n              \"success\": false,\n              \"message\": \"assert_not_own_property: unexpected property \\\"sec-fetch-mode\\\" is found on object\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no init\",\n              \"success\": true\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent), no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade, no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade, no init\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n            }\n          ]\n        },\n        \"form-submission.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"form-submission.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - GET\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - POST\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"header-link.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=icon\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=stylesheet\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=icon\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=icon\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=stylesheet\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=icon - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=stylesheet - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=icon - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=stylesheet - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"header-link.https.sub.tentative.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-dest rel=stylesheet\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"header-link.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site rel=icon - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=icon - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=icon - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=icon - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=icon - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=icon - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=icon - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=icon - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=stylesheet - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=icon - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=stylesheet - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=icon - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user rel=stylesheet - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=icon - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=stylesheet - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=icon - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=stylesheet - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=icon - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access rel=stylesheet - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=icon - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site rel=stylesheet - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"header-refresh.https.optional.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"header-refresh.optional.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"script-json-module-import-static.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"script-json-module-import-static.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"script-module-import-dynamic.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            }\n          ]\n        },\n        \"script-module-import-dynamic.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.\\\"\"\n            }\n          ]\n        },\n        \"script-module-import-static.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"script-module-import-static.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"svg-image.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin=anonymous\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode attributes: crossorigin=use-credentials\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"svg-image.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade no attributes\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"window-history.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same origin - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"window-history.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - history.back\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - history.forward\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"window-location.https.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"window-location.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent) - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - location\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - location.href\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - location.assign\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade - location.replace\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            }\n          ]\n        },\n        \"worker-dedicated-constructor.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-mode - no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - options: type=module\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - options: type=module\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - options: type=module\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        },\n        \"worker-dedicated-constructor.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination, no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination, no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no options\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        },\n        \"worker-dedicated-importscripts.https.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Same origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Cross-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Origin -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same Origin\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Same-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Same-Site -> Cross-Site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Cross-site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Same site\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        },\n        \"worker-dedicated-importscripts.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-mode - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-user - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy same-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade (header not sent)\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS upgrade\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-site - HTTPS downgrade-upgrade\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n            }\n          ]\n        }\n      },\n      \"navigation.https.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"object.https.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-Origin object\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-Site object\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-Site object\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"paint-worklet.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"The fetch metadata for paint worklet\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: CSS is not defined\\\"\"\n          }\n        ]\n      },\n      \"preload.https.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Browser supports preload.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='fetch' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='fetch' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='fetch' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='font' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='font' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='font' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='image' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='image' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='image' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='script' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='script' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='script' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='style' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='style' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='style' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='track' href='https://web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='track' href='https://www.web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<link rel='preload' as='track' href='https://www.not-web-platform.test:8443/...'>\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"redirect\": {\n        \"multiple-redirect-https-downgrade-upgrade.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Https downgrade-upgrade top level navigation\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"Https downgrade-upgrade embed\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"Https downgrade-upgrade object\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"redirect-http-upgrade.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Http upgrade top level navigation\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"Http upgrade embed\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"Http upgrade object\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        },\n        \"redirect-https-downgrade.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Https downgrade top level navigation\",\n              \"success\": false,\n              \"message\": \"window.open is not a function\"\n            },\n            {\n              \"name\": \"Https downgrade embed\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"Https downgrade object\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            }\n          ]\n        }\n      },\n      \"report.https.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"style.https.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-Origin style\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-Site style\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-Site style\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-Origin, cors style\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"track.https.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-Origin track\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-Site track\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Cross-Site track\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Same-Origin, CORS track\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"trailing-dot.https.sub.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Fetching a resource from the same origin, but spelled with a trailing dot.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Fetching a resource from the same site, but spelled with a trailing dot.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Fetching a resource from a cross-site host, spelled with a trailing dot.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          }\n        ]\n      },\n      \"unload.https.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.https.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker.https.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Same-Origin worker\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Worker is not defined\\\"\"\n          }\n        ]\n      },\n      \"xslt.https.sub.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"nosniff\": {\n      \"image.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"URL query: null\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: image/gif\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: image/png\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: image/png;blah\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: image/svg+xml\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: application/xml\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: application/blah+xml\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"importscripts.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Test importScripts()\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          }\n        ]\n      },\n      \"parsing-nosniff.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Loading JSON…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20NOSNIFF\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"x-content-type-OPTIONS%3A%20nosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cnosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%0D%0AX-Content-Type-Options%3A%20no\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20no%0D%0AX-Content-Type-Options%3A%20nosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%0D%0AX-Content-Type-Options%3A%20nosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%0D%0AX-Content-Type-Options%3A%20nosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20%2Cnosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%0C\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%0B\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20nosniff%0B%2Cnosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20'NosniFF'\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"X-Content-Type-Options%3A%20%22nosniFF%22\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Content-Type-Options%3A%20nosniff\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"script.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"URL query: null\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/json\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/javascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/ecmascript\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/ecmascript;blah\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/javascript1.0\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"stylesheet.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"URL query: null\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/json\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: null\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: x/x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: text/json\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/css\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/css;charset=utf-8\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"URL query: text/css;blah\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: text/css\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: text/css;charset=utf-8\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Revalidated URL query: text/css;blah\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"worker.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"URL query: \",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=x\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=x/x\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=text/html\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=text/json\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=text/javascript\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=text/ecmascript\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          },\n          {\n            \"name\": \"URL query: ?type=text/ecmascript;yay\",\n            \"success\": false,\n            \"message\": \"Worker is not defined\"\n          }\n        ]\n      }\n    },\n    \"orb\": {\n      \"tentative\": {\n        \"compressed-image-sniffing.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ORB shouldn't block compressed images\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"content-range.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ORB shouldn't block opaque range of image/png starting at zero: fetch(..., {mode: \\\"no-cors\\\"})\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ORB should block opaque range of image/png not starting at zero, that isn't subsequent: fetch(..., {mode: \\\"no-cors\\\"})\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Should have rejected: fetch(..., {mode: \\\"no-cors\\\"}) Reached unreachable code\"\n            }\n          ]\n        },\n        \"img-mime-types-coverage.tentative.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/css'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'image/svg+xml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'application/ecmascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'application/javascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'application/x-ecmascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'application/x-javascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/ecmascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.0'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.1'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.2'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.3'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.4'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/javascript1.5'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/jscript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/livescript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/x-ecmascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should allow the response if Content-Type is: 'text/x-javascript'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/html'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/json'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/json'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/ld+json'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/xml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/xml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/xhtml+xml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/dash+xml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/gzip'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/msexcel'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/mspowerpoint'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/msword'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/msword-template'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/pdf'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.apple.mpegurl'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ces-quickpoint'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ces-quicksheet'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ces-quickword'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-excel'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-excel.sheet.macroenabled.12'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-powerpoint'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-powerpoint.presentation.macroenabled.12'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-word'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-word.document.12'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.ms-word.document.macroenabled.12'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.msword'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.presentationml.template'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.presentation-openxml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.presentation-openxmlm'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.spreadsheet-openxml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/vnd.wordprocessing-openxml'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/x-gzip'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/x-protobuf'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/x-protobuffer'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'application/zip'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'audio/mpegurl'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'multipart/byteranges'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'multipart/signed'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/event-stream'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/csv'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"ORB should block the response if Content-Type is: 'text/vtt'.  \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"known-mime-type.sub.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"nosniff.sub.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"script-js-unlabeled-gzipped.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"script-unlabeled.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"script-utf16-without-bom-hint-charset.sub.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"status.sub.any.html\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"status.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ORB should block initial media requests with status not 200 or 206\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"unknown-mime-type.sub.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"ORB shouldn't block opaque failed missing MIME type (font/ttf): fetch(..., {mode: \\\"no-cors\\\"})\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ORB shouldn't block opaque failed missing MIME type (text/plain): fetch(..., {mode: \\\"no-cors\\\"})\",\n              \"success\": true\n            },\n            {\n              \"name\": \"ORB shouldn't block opaque failed missing MIME type (application/json): fetch(..., {mode: \\\"no-cors\\\"})\",\n              \"success\": true\n            }\n          ]\n        }\n      }\n    },\n    \"origin\": {\n      \"assorted.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Origin header and 308 redirect\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"null\\\" but got \\\"http://web-platform.test:8000\\\"\"\n          },\n          {\n            \"name\": \"Origin header and GET navigation\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST navigation\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin navigation with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"null\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin navigation with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"null\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"no Origin header\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin navigation with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch no-cors mode with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch cors mode with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and GET same-origin fetch cors mode with Referrer-Policy same-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin navigation with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"null\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch cors mode with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Origin header and GET cross-origin fetch cors mode with Referrer-Policy same-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"no Origin header\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin navigation with Referrer-Policy origin-when-cross-origin\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET same-origin fetch cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin navigation with Referrer-Policy origin-when-cross-origin\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET cross-origin fetch cors mode with Referrer-Policy origin-when-cross-origin\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"no Origin header\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin navigation with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET same-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin navigation with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET cross-origin fetch cors mode with Referrer-Policy no-referrer-when-downgrade\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"no Origin header\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin navigation with Referrer-Policy unsafe-url\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch no-cors mode with Referrer-Policy unsafe-url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST same-origin fetch cors mode with Referrer-Policy unsafe-url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET same-origin fetch cors mode with Referrer-Policy unsafe-url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin navigation with Referrer-Policy unsafe-url\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch no-cors mode with Referrer-Policy unsafe-url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and POST cross-origin fetch cors mode with Referrer-Policy unsafe-url\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Origin header and GET cross-origin fetch cors mode with Referrer-Policy unsafe-url\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"http://web-platform.test:8000\\\" but got \\\"no Origin header\\\"\"\n          }\n        ]\n      }\n    },\n    \"private-network-access\": {\n      \"anchor.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"anchor.tentative.https.window.html?include=from-private\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"anchor.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"anchor.tentative.https.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"anchor.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch-from-treat-as-public.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch.tentative.https.window.html?include=baseline\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch.tentative.https.window.html?include=from-private\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"fetch.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-private\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.https.window.html?include=grandparent\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"iframe.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"nested-worker.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"nested-worker.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"preflight-cache.https.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"redirect.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-background-fetch.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-fetch-document-treat-as-public.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-fetch-document.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-fetch.tentative.https.window.html?1-8\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-fetch.tentative.https.window.html?9-last\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker-update.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"service-worker.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker-blob-fetch.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker-blob-fetch.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker-fetch.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker-fetch.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"shared-worker.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"websocket.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"websocket.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open-existing.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.tentative.https.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.tentative.https.window.html?include=from-private\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.tentative.https.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.tentative.https.window.html?include=from-treat-as-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"window-open.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker-blob-fetch.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker-fetch.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker-fetch.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"worker.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"xhr-from-treat-as-public.tentative.https.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"xhr.https.tentative.window.html?include=from-local\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"xhr.https.tentative.window.html?include=from-private\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"xhr.https.tentative.window.html?include=from-public\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"xhr.tentative.window.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"range\": {\n      \"blob.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"A simple blob range request.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"A blob range request with no type.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"A blob range request with no end.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"A blob range request with no start.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"A simple blob range request with whitespace.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob content with short content and a large range end\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob content with short content and a range end matching content length\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with whitespace before and after hyphen\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with whitespace after hyphen\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with whitespace around equals sign\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Blob range with no value\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with incorrect range header\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with incorrect range header #2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with incorrect range header #3\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range request with multiple range values\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range request with multiple range values and whitespace\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range request with trailing comma\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range with no start or end\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range request with short range end\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range start should be an ASCII digit\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range should have a dash\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range end should be an ASCII digit\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range should include '-'\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range should include '='\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob range should include 'bytes='\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob content with short content and a large range start\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Blob content with short content and a range start matching the content length\",\n            \"success\": true\n          }\n        ]\n      },\n      \"data.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"data: URL and Range header\",\n            \"success\": true\n          },\n          {\n            \"name\": \"data: URL and Range header with multiple ranges\",\n            \"success\": true\n          }\n        ]\n      },\n      \"general.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Range header setting allowed for guard type: none\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Range header setting allowed for guard type: response\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Range header setting allowed for guard type: request\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Privileged header not allowed for guard type: request-no-cors\",\n            \"success\": false,\n            \"message\": \"assert_false: expected false got true\"\n          },\n          {\n            \"name\": \"Fetch with range header will be sent with Accept-Encoding: identity\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Cross Origin Fetch with non safe range header\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: \\\"loaded with range header bytes=10-9\\\"\"\n          },\n          {\n            \"name\": \"Cross Origin Fetch with safe range header\",\n            \"success\": true\n          }\n        ]\n      },\n      \"general.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Script executed from partial response\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Fetch with range header will be sent with Accept-Encoding: identity\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.addEventListener is not a function\\\"\"\n          }\n        ]\n      },\n      \"non-matching-range-response.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Range requests with no rewrites should succeed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Range response out of range of request should succeed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Range requests ignored (200 status) should succeed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      },\n      \"sw.https.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Defer range header filter tests to service worker\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Defer range header passthrough tests to service worker\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Ranged response not allowed following no-cors ranged request\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Non-opaque ranged response executed\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Accept-Encoding should not appear in a service worker\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          },\n          {\n            \"name\": \"Opaque range preload successes and failures should be indistinguishable\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      }\n    },\n    \"redirect-navigate\": {\n      \"302-found-post.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTTP 302 Found POST Navigation\",\n            \"success\": false,\n            \"message\": \"window.addEventListener is not a function\"\n          }\n        ]\n      },\n      \"preserve-fragment.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"redirects\": {\n      \"data.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"<img> fetch that redirects to data: URL\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"<script> fetch that redirects to data: URL\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"XMLHttpRequest fetch that redirects to data: URL\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"subresource-fragments.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"security\": {\n      \"1xx-response.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Status(100) should be ignored.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Status(101) should be accepted, with removing body.\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Status(103) should be ignored.\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Status(199) should be ignored.\",\n            \"success\": true\n          }\n        ]\n      },\n      \"dangling-markup\": {\n        \"dangling-markup-mitigation-allowed-apis.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Does not block window.open(`resources/empty.html?\\n<`,'_self')\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Does not block location.replace(`resources/empty.html?\\n<`)\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Setup controlled frame\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n            },\n            {\n              \"name\": \"Does not block xhr\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n            },\n            {\n              \"name\": \"Does not block EventSource\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n            },\n            {\n              \"name\": \"Does not block fetch\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n            },\n            {\n              \"name\": \"Does not block Worker\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n            },\n            {\n              \"name\": \"Does not block importScripts\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'contentWindow')\\\"\"\n            },\n            {\n              \"name\": \"Clean up iframe\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'remove')\\\"\"\n            },\n            {\n              \"name\": \"Does not block new URL()\",\n              \"success\": true\n            }\n          ]\n        },\n        \"dangling-markup-mitigation-data-url.tentative.sub.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"data:image/png;base64,&#10;iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"data:image/png;base64,i&#10;VBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"data:image/svg+xml;utf8,\\\\n      <svg width='1' height='1' xmlns='http://www.w3.org/2000/svg'>\\\\n        <rect width='100%' height='100%' fill='rebeccapurple'/>\\\\n        <rect x='10%' y='10%' width='80%' height='80%' fill='lightgreen'/>\\\\n      </svg>\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/green-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&amp;amp;lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/green-256x256.png?&amp;amp;%2310;&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"     data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"\\\\ndata:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"&#10;data:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"\\\\tdata:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<iframe id=\\\"dangling\\\"\\\\n        src=\\\"\\\\rdata:text/html,\\\\n            <img\\\\n              onload='window.parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'\\\\n              onerror='window.parent.postMessage(&quot;error&quot;, &quot;*&quot;);'\\\\n              src='http://web-platform.test:8000/images/gr&#10;een-256x256.png?&lt;'>\\\\n        \\\">\\\\n     </iframe>\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"dangling-markup-mitigation.tentative.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Fetch: /images/green-1x1.png\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\nen-1x1.png\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\ten-1x1.png\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\ren-1x1.png\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?img=<\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?img=&lt;\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?img=%3C\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\neen-1x1.png?img=%3C\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\reen-1x1.png?img=%3C\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\teen-1x1.png?img=%3C\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?img=&#10;\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\neen-1x1.png?img=&#10;\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\reen-1x1.png?img=&#10;\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gr\\\\teen-1x1.png?img=&#10;\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\nen-1x1.png?img=<\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\ren-1x1.png?img=<\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch: /images/gre\\\\ten-1x1.png?img=<\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?<\\\\n=block\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?<\\\\r=block\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"Fetch: /images/green-1x1.png?<\\\\t=block\",\n              \"success\": false,\n              \"message\": \"assert_unreached: Fetch should fail. Reached unreachable code\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&lt;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&#10;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&amp;#10;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&amp;lt;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&amp;#10;b&amp;lt;c\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"\\\\n      <img id=\\\"dangling\\\" src=\\\"\\\\n        /images/green-1x1.png?img=\\\\n      \\\">\\\\n    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"\\\\n      <img id=\\\"dangling\\\" src=\\\"\\\\n        /images/green-1x1.png?img=&amp;lt;\\\\n      \\\">\\\\n    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"\\\\n      <img id=\\\"dangling\\\" src=\\\"\\\\n        /images/green-1x1.png?img=&amp;#10;\\\\n      \\\">\\\\n    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&#10;&lt;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"<img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=&lt;&#10;b\\\">\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"\\\\n      <img id=\\\"dangling\\\" src=\\\"/images/green-1x1.png?img=\\\\n        &lt;\\\\n        &#10;b\\\\n      \\\">\\\\n    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"dangling-markup-mitigation.tentative.https.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Only blocks dangling markup requests\",\n              \"success\": false,\n              \"message\": \"Cannot read properties of undefined (reading 'register')\"\n            }\n          ]\n        },\n        \"media.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"Should load audio\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should not load audio with dangling markup in URL\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should load video\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"Should not load video with dangling markup in URL\",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"option.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">        <input type=\\\"submit\\\">        <select name=\\\"dangling\\\"><option>    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <div>        <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">          <input type=\\\"submit\\\">          <select name=\\\"dangling\\\"><option>    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\" id=\\\"form\\\">        <input type=\\\"submit\\\">      </form>      <select name=\\\"dangling\\\" form=\\\"form\\\"><option>    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">        <input type=\\\"submit\\\">        <select name=\\\"dangling\\\"><option label=\\\"yay\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <div>        <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">          <input type=\\\"submit\\\">          <select name=\\\"dangling\\\"><option label=\\\"yay\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\" id=\\\"form\\\">        <input type=\\\"submit\\\">      </form>      <select name=\\\"dangling\\\" form=\\\"form\\\"><option label=\\\"yay\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        },\n        \"textarea.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">        <input type=\\\"submit\\\">        <textarea name=\\\"dangling\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <div>        <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\">          <input type=\\\"submit\\\">          <textarea name=\\\"dangling\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            },\n            {\n              \"name\": \"      <form action=\\\"/resource-timing/resources/document-navigated.html\\\" method=\\\"post\\\" id=\\\"form\\\">        <input type=\\\"submit\\\">      </form>      <textarea name=\\\"dangling\\\" form=\\\"form\\\">    \",\n              \"success\": false,\n              \"message\": \"document is not defined\"\n            }\n          ]\n        }\n      },\n      \"embedded-credentials.tentative.sub.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Embedded credentials are treated as network errors.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Embedded credentials are treated as network errors in frames.\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"Embedded credentials are treated as network errors in new windows.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"Embedded credentials matching the top-level are not treated as network errors for relative URLs.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"Embedded credentials matching the top-level are not treated as network errors for same-origin URLs.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"Embedded credentials matching the top-level are treated as network errors for cross-origin URLs.\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"redirect-to-url-with-credentials.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"No CORS fetch after a redirect with an URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"CORS fetch after a redirect with a cross origin URL containing credentials\",\n            \"success\": true\n          },\n          {\n            \"name\": \"CORS fetch after a redirect with a same origin URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: fetch failed\\\"\"\n          },\n          {\n            \"name\": \"Image loading after a redirect with an URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Image is not defined\\\"\"\n          },\n          {\n            \"name\": \"CORS Image loading after a redirect with a cross origin URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Image is not defined\\\"\"\n          },\n          {\n            \"name\": \"CORS Image loading after a redirect with a same origin URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: Image is not defined\\\"\"\n          },\n          {\n            \"name\": \"Frame loading after a redirect with an URL containing credentials\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n          }\n        ]\n      }\n    },\n    \"stale-while-revalidate\": {\n      \"fetch-sw.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Second fetch returns same response\",\n            \"success\": false,\n            \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: Cannot read properties of undefined (reading 'register')\\\"\"\n          }\n        ]\n      },\n      \"fetch.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Second fetch returns same response\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"qoidijwtsfvgrbpjvqow\\\" but got \\\"pyimmkhbwsuwcvgrysyj\\\"\"\n          }\n        ]\n      },\n      \"revalidate-not-blocked-by-csp.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"stale-css.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"stale-image.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"stale-script.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    }\n  },\n  \"mimesniff\": {\n    \"media\": {\n      \"media-sniff.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-raw.mp3 loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp3-with-id3.mp3 loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"flac.flac loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"ogg.ogg loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"mp4.mp4 loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"wav.wav loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type \",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type bogus/mime\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type application/octet-stream\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type text/html\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type audio/ogg; codec=vorbis\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"webm.webm loads when served with Content-Type application/pdf\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      }\n    },\n    \"mime-types\": {\n      \"charset-parameter.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Loading data…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"TEXT/HTML;CHARSET=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk(\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;x=(;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk;charset=windows-1255\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=();charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset =gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html ;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html; charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset= gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset= \\\"gbk\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\u000bgbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\fgbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;\\u000bcharset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;\\fcharset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset='gbk'\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset='gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk'\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=';charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;test;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;test=;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;';charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;\\\";charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html ; ; charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;;;;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset= \\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=foo\\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\" gbk\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk \\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\ gbk\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\g\\\\b\\\\k\\\"\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\"x\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=GBK\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;charset={gbk}\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"text/html;test=ÿ;charset=gbk\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"parsing.any.html\": {\n        \"success\": \"flaky\",\n        \"cases\": [\n          {\n            \"name\": \"Loading data…\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"TEXT/HTML;CHARSET=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"TEXT/HTML;CHARSET=GBK (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk( (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\"gbk(\\\\\\\"\\\" but got \\\"text/html;charset=gbk(\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk( (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;x=(;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;x=\\\\\\\"(\\\\\\\";charset=gbk\\\" but got \\\"text/html;x=(;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;x=(;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk;charset=windows-1255 (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;charset=gbk;charset=windows-1255\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk;charset=windows-1255 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=();charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\"()\\\\\\\"\\\" but got \\\"text/html;charset=();charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=();charset=GBK (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset =gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html\\\" but got \\\"text/html;charset =gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset =gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html ;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html ;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html ;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html; charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html; charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html; charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset= gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\" gbk\\\\\\\"\\\" but got \\\"text/html;charset= gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset= gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset= \\\"gbk\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\" \\\\\\\\\\\\\\\"gbk\\\\\\\\\\\\\\\"\\\\\\\"\\\" but got \\\"text/html;charset= \\\\\\\"gbk\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset= \\\"gbk\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\u000bgbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\u000bgbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\fgbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\fgbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;\\u000bcharset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;\\u000bcharset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;\\fcharset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;\\fcharset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset='gbk' (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset='gbk' (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset='gbk (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset='gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk' (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk' (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=';charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset='\\\" but got \\\"text/html;charset=';charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=';charset=GBK (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;test;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;test;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;test;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;test=;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;test=;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;test=;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;';charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;';charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;';charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;\\\";charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;\\\\\\\";charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;\\\";charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html ; ; charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html ; ; charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html ; ; charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;;;;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;;;;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;;;;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset= \\\";charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=GBK\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset= \\\";charset=GBK (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=foo\\\";charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=GBK\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=foo\\\";charset=GBK (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=GBK\\\" but got \\\"text/html;charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;charset=\\\\\\\"gbk\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;charset=\\\\\\\"gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=gbk\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\"gbk\\\\\\\\\\\\\\\"\\\\\\\"\\\" but got \\\"text/html;charset=gbk\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=gbk\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\" gbk\\\" (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\" gbk\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk \\\" (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk \\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\ gbk\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\" gbk\\\\\\\"\\\" but got \\\"text/html;charset=\\\\\\\"\\\\\\\\ gbk\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\ gbk\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\g\\\\b\\\\k\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;charset=\\\\\\\"\\\\\\\\g\\\\\\\\b\\\\\\\\k\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\\g\\\\b\\\\k\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\"x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=gbk\\\" but got \\\"text/html;charset=\\\\\\\"gbk\\\\\\\"x\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"gbk\\\"x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\";charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\"\\\\\\\"\\\" but got \\\"text/html;charset=\\\\\\\"\\\\\\\";charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\"\\\";charset=GBK (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=GBK (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\";charset=GBK\\\\\\\"\\\" but got \\\"text/html;charset=\\\\\\\";charset=gbk\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset=\\\";charset=GBK (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;charset=\\\\\\\";charset=GBK\\\\\\\"\\\" but got \\\"text/html;charset=\\\\\\\";charset=gbk\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset={gbk} (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;charset=\\\\\\\"{gbk}\\\\\\\"\\\" but got \\\"text/html;charset={gbk}\\\"\"\n          },\n          {\n            \"name\": \"text/html;charset={gbk} (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;a]=bar;b[=bar;c=bar (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;c=bar\\\" but got \\\"text/html;a]=bar;b[=bar;c=bar\\\"\"\n          },\n          {\n            \"name\": \"text/html;a]=bar;b[=bar;c=bar (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;valid=\\\";\\\";foo=bar (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;valid=\\\";\\\";foo=bar (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;in]valid=\\\";asd=foo\\\";foo=bar (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;foo=bar\\\" but got \\\"text/html;in]valid=\\\\\\\";asd=foo\\\\\\\";foo=bar\\\"\"\n          },\n          {\n            \"name\": \"text/html;in]valid=\\\";asd=foo\\\";foo=bar (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\\\" but got \\\"!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\\\"\"\n          },\n          {\n            \"name\": \"!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\t !\\\\\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\t !\\\\\\\\\\\\\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\\\\\\\"\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\t !\\\\\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\\\" (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\t !\\\\\\\\\\\\\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\\\\\\\"\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;test (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x\\\" but got \\\"x/x;test\\\"\"\n          },\n          {\n            \"name\": \"x/x;test (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;test=\\\"\\\\ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;test=\\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\"\\\" but got \\\"x/x;test=\\\\\\\"\\\\\\\\\\\"\"\n          },\n          {\n            \"name\": \"x/x;test=\\\"\\\\ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=  (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x\\\" but got \\\"x/x;x= \\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\t (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x\\n\\r\\t ;x=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x\\n\\r\\t ;x=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\n\\r\\t x/x;x=x\\n\\r\\t  (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\n\\r\\t x=x\\n\\r\\t ;x=y (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\n\\r\\t x=x\\n\\r\\t ;x=y (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html;test=ÿ;charset=gbk (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"text/html;test=\\\\\\\"ÿ\\\\\\\";charset=gbk\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"text/html;test=ÿ;charset=gbk (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"text/html;test=\\\\\\\"ÿ\\\\\\\";charset=gbk\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;test=�;x=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;test=�;x=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000bx/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000bx/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\fx/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\fx/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x\\u000b (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x\\u000b (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x\\f (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x\\f (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \" (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\t (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"/\\\"\"\n          },\n          {\n            \"name\": \"/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"bogus (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"bogus\\\"\"\n          },\n          {\n            \"name\": \"bogus (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"bogus/ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"bogus/\\\"\"\n          },\n          {\n            \"name\": \"bogus/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"bogus/  (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"bogus/ \\\"\"\n          },\n          {\n            \"name\": \"bogus/bogus/; (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"bogus/bogus/;\\\"\"\n          },\n          {\n            \"name\": \"bogus/bogus/; (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"</> (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"</>\\\"\"\n          },\n          {\n            \"name\": \"</> (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"(/) (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"(/)\\\"\"\n          },\n          {\n            \"name\": \"(/) (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ÿ/ÿ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ÿ/ÿ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/html(;doesnot=matter (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"text/html(;doesnot=matter\\\"\"\n          },\n          {\n            \"name\": \"text/html(;doesnot=matter (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"{/} (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"{/}\\\"\"\n          },\n          {\n            \"name\": \"{/} (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ā/Ā (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ā/Ā (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text /html (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"text /html\\\"\"\n          },\n          {\n            \"name\": \"text /html (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"text/ html (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"text/ html\\\"\"\n          },\n          {\n            \"name\": \"text/ html (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"text/html\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"\\\\\\\"text/html\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"\\\"text/html\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0000/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0000/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0000 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0000 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0000=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0000=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0000;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0000;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0000\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0000\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0001/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0001/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0001 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0001 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0001=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0001=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0001;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0001;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0001\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0001\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0002/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0002/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0002 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0002 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0002=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0002=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0002;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0002;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0002\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0002\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0003/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0003/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0003 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0003 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0003=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0003=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0003;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0003;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0003\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0003\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0004/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0004/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0004 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0004 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0004=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0004=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0004;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0004;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0004\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0004\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0005/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0005/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0005 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0005 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0005=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0005=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0005;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0005;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0005\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0005\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0006/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0006/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0006 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0006 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0006=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0006=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0006;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0006;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0006\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0006\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0007/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0007/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0007 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0007 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0007=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0007=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0007;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0007;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0007\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0007\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\b/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\b/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\b (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\b (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\b=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\b=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\b;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\b;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\b\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\b\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\t/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\t (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\t=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\t=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\n/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\n (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\n=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\n=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\n;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\n;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\n\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\n\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000b/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000b/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000b (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000b (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u000b=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u000b=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u000b;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u000b;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000b\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000b\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\f/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\f/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\f (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\f (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\f=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\f=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\f;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\f;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\f\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\f\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\r/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\r (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\r=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\r=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\r;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\r;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\r\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\r\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000e/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000e/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000e (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000e (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u000e=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u000e=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u000e;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u000e;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000e\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000e\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000f/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u000f/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000f (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u000f (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u000f=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u000f=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u000f;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u000f;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000f\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u000f\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0010/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0010/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0010 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0010 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0010=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0010=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0010;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0010;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0010\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0010\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0011/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0011/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0011 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0011 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0011=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0011=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0011;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0011;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0011\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0011\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0012/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0012/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0012 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0012 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0012=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0012=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0012;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0012;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0012\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0012\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0013/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0013/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0013 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0013 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0013=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0013=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0013;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0013;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0013\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0013\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0014/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0014/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0014 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0014 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0014=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0014=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0014;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0014;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0014\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0014\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0015/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0015/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0015 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0015 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0015=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0015=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0015;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0015;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0015\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0015\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0016/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0016/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0016 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0016 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0016=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0016=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0016;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0016;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0016\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0016\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0017/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0017/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0017 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0017 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0017=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0017=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0017;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0017;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0017\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0017\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0018/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0018/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0018 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0018 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0018=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0018=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0018;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0018;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0018\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0018\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0019/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u0019/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0019 (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u0019 (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u0019=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u0019=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u0019;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u0019;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0019\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u0019\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001a/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001a/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001a (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001a (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001a=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001a=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001a;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001a;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001a\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001a\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001b/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001b/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001b (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001b (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001b=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001b=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001b;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001b;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001b\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001b\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001c/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001c/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001c (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001c (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001c=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001c=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001c;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001c;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001c\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001c\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001d/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001d/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001d (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001d (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001d=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001d=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001d;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001d;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001d\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001d\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001e/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001e/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001e (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001e (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001e=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001e=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001e;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001e;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001e\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001e\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001f/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\u001f/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001f (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\u001f (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\u001f=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\u001f=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\u001f;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\u001f;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001f\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\u001f\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \" /x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\" /x\\\"\"\n          },\n          {\n            \"name\": \"x/  (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/ \\\"\"\n          },\n          {\n            \"name\": \"x/x; =x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x; =x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x; =x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\"/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"\\\\\\\"/x\\\"\"\n          },\n          {\n            \"name\": \"\\\"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\\" (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/\\\\\\\"\\\"\"\n          },\n          {\n            \"name\": \"x/\\\" (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\\"=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;\\\\\\\"=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\\"=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"(/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"(/x\\\"\"\n          },\n          {\n            \"name\": \"(/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/( (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/(\\\"\"\n          },\n          {\n            \"name\": \"x/( (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;(=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;(=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;(=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=(;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"(\\\\\\\";bonus=x\\\" but got \\\"x/x;x=(;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=(;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"(\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"(\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \")/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\")/x\\\"\"\n          },\n          {\n            \"name\": \")/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/) (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/)\\\"\"\n          },\n          {\n            \"name\": \"x/) (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;)=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;)=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;)=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=);bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\")\\\\\\\";bonus=x\\\" but got \\\"x/x;x=);bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=);bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\")\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\")\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \",/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\",/x\\\"\"\n          },\n          {\n            \"name\": \",/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/, (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/,\\\"\"\n          },\n          {\n            \"name\": \"x/, (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;,=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;,=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;,=x;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;bonus=x\\\" but got \\\"x/x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=,;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\",\\\\\\\";bonus=x\\\" but got \\\"x/x;x=,;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=,;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\",\\\\\\\";bonus=x\\\" but got \\\"x/x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\",\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\",\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;/=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;/=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;/=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=/;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"/\\\\\\\";bonus=x\\\" but got \\\"x/x;x=/;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=/;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"/\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"/\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \":/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\":/x\\\"\"\n          },\n          {\n            \"name\": \":/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/: (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/:\\\"\"\n          },\n          {\n            \"name\": \"x/: (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;:=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;:=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;:=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=:;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\":\\\\\\\";bonus=x\\\" but got \\\"x/x;x=:;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=:;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\":\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\":\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \";/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\";/x\\\"\"\n          },\n          {\n            \"name\": \";/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/; (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/;\\\"\"\n          },\n          {\n            \"name\": \"x/; (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"</x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"</x\\\"\"\n          },\n          {\n            \"name\": \"</x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/< (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/<\\\"\"\n          },\n          {\n            \"name\": \"x/< (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;<=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;<=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;<=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=<;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"<\\\\\\\";bonus=x\\\" but got \\\"x/x;x=<;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=<;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"<\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"<\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"=/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"=/x\\\"\"\n          },\n          {\n            \"name\": \"=/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/= (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/=\\\"\"\n          },\n          {\n            \"name\": \"x/= (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x==;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"=\\\\\\\";bonus=x\\\" but got \\\"x/x;x==;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x==;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"=\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"=\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \">/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\">/x\\\"\"\n          },\n          {\n            \"name\": \">/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/> (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/>\\\"\"\n          },\n          {\n            \"name\": \"x/> (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;>=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;>=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;>=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=>;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\">\\\\\\\";bonus=x\\\" but got \\\"x/x;x=>;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=>;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\">\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\">\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"?/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"?/x\\\"\"\n          },\n          {\n            \"name\": \"?/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/? (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/?\\\"\"\n          },\n          {\n            \"name\": \"x/? (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;?=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;?=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;?=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=?;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"?\\\\\\\";bonus=x\\\" but got \\\"x/x;x=?;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=?;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"?\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"?\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"@/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"@/x\\\"\"\n          },\n          {\n            \"name\": \"@/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/@ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/@\\\"\"\n          },\n          {\n            \"name\": \"x/@ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;@=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;@=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;@=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=@;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"@\\\\\\\";bonus=x\\\" but got \\\"x/x;x=@;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=@;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"@\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"@\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"[/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"[/x\\\"\"\n          },\n          {\n            \"name\": \"[/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/[ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/[\\\"\"\n          },\n          {\n            \"name\": \"x/[ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;[=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;[=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;[=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=[;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"[\\\\\\\";bonus=x\\\" but got \\\"x/x;x=[;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=[;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"[\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"[\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"\\\\/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"\\\\\\\\/x\\\"\"\n          },\n          {\n            \"name\": \"\\\\/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/\\\\ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/\\\\\\\\\\\"\"\n          },\n          {\n            \"name\": \"x/\\\\ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;\\\\=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;\\\\\\\\=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;\\\\=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"]/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"]/x\\\"\"\n          },\n          {\n            \"name\": \"]/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/] (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/]\\\"\"\n          },\n          {\n            \"name\": \"x/] (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;]=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;]=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;]=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=];bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"]\\\\\\\";bonus=x\\\" but got \\\"x/x;x=];bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=];bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"]\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"]\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"{/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"{/x\\\"\"\n          },\n          {\n            \"name\": \"{/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/{ (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/{\\\"\"\n          },\n          {\n            \"name\": \"x/{ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;{=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;{=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;{=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x={;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"{\\\\\\\";bonus=x\\\" but got \\\"x/x;x={;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x={;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"{\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"{\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"}/x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"}/x\\\"\"\n          },\n          {\n            \"name\": \"}/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/} (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"\\\" but got \\\"x/}\\\"\"\n          },\n          {\n            \"name\": \"x/} (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;}=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"x/x;}=x;bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;}=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=};bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"}\\\\\\\";bonus=x\\\" but got \\\"x/x;x=};bonus=x\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=};bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"}\\\";bonus=x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"}\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \" /x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \" /x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/  (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/  (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x; =x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x; =x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x= ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\" \\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x= ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\" \\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\" \\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\" \\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\" \\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\" \\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¡/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¡/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¡ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¡ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¡=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¡=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¡;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¡\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¡;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¡\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¡\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¡\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¡\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¡\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¢/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¢/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¢ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¢ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¢=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¢=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¢;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¢\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¢;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¢\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¢\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¢\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¢\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¢\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"£/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"£/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/£ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/£ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;£=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;£=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=£;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"£\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=£;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"£\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"£\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"£\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"£\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"£\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¤/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¤/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¤ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¤ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¤=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¤=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¤;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¤\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¤;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¤\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¤\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¤\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¤\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¤\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¥/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¥/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¥ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¥ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¥=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¥=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¥;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¥\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¥;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¥\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¥\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¥\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¥\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¥\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¦/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¦/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¦ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¦ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¦=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¦=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¦;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¦\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¦;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¦\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¦\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¦\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¦\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¦\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"§/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"§/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/§ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/§ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;§=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;§=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=§;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"§\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=§;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"§\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"§\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"§\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"§\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"§\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¨/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¨/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¨ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¨ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¨=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¨=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¨;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¨\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¨;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¨\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¨\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¨\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¨\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¨\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"©/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"©/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/© (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/© (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;©=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;©=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=©;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"©\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=©;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"©\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"©\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"©\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"©\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"©\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ª/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ª/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ª (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ª (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ª=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ª=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ª;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ª\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ª;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ª\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ª\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ª\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ª\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ª\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"«/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"«/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/« (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/« (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;«=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;«=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=«;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"«\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=«;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"«\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"«\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"«\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"«\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"«\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¬/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¬/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¬ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¬ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¬=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¬=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¬;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¬\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¬;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¬\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¬\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¬\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¬\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¬\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"­/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"­/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/­ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/­ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;­=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;­=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=­;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"­\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=­;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"­\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"­\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"­\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"­\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"­\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"®/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"®/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/® (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/® (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;®=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;®=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=®;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"®\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=®;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"®\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"®\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"®\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"®\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"®\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¯/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¯/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¯ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¯ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¯=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¯=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¯;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¯\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¯;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¯\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¯\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¯\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¯\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¯\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"°/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"°/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/° (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/° (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;°=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;°=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=°;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"°\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=°;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"°\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"°\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"°\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"°\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"°\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"±/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"±/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/± (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/± (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;±=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;±=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=±;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"±\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=±;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"±\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"±\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"±\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"±\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"±\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"²/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"²/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/² (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/² (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;²=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;²=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=²;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"²\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=²;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"²\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"²\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"²\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"²\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"²\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"³/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"³/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/³ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/³ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;³=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;³=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=³;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"³\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=³;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"³\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"³\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"³\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"³\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"³\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"´/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"´/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/´ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/´ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;´=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;´=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=´;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"´\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=´;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"´\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"´\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"´\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"´\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"´\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"µ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"µ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/µ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/µ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;µ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;µ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=µ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"µ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=µ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"µ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"µ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"µ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"µ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"µ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¶/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¶/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¶ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¶ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¶=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¶=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¶;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¶\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¶;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¶\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¶\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¶\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¶\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¶\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"·/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"·/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/· (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/· (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;·=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;·=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=·;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"·\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=·;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"·\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"·\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"·\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"·\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"·\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¸/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¸/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¸ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¸ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¸=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¸=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¸;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¸\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¸;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¸\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¸\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¸\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¸\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¸\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¹/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¹/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¹ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¹ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¹=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¹=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¹;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¹\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¹;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¹\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¹\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¹\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¹\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¹\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"º/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"º/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/º (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/º (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;º=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;º=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=º;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"º\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=º;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"º\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"º\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"º\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"º\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"º\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"»/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"»/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/» (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/» (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;»=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;»=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=»;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"»\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=»;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"»\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"»\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"»\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"»\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"»\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¼/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¼/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¼ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¼ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¼=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¼=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¼;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¼\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¼;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¼\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¼\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¼\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¼\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¼\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"½/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"½/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/½ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/½ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;½=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;½=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=½;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"½\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=½;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"½\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"½\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"½\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"½\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"½\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¾/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¾/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¾ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¾ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¾=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¾=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¾;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¾\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¾;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¾\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¾\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¾\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¾\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¾\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"¿/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"¿/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¿ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/¿ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;¿=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;¿=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=¿;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¿\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=¿;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¿\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¿\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"¿\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"¿\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"¿\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"À/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"À/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/À (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/À (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;À=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;À=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=À;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"À\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=À;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"À\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"À\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"À\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"À\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"À\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Á/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Á/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Á (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Á (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Á=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Á=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Á;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Á;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Á\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Á\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Â/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Â/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Â (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Â (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Â=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Â=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Â;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Â;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Â\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Â\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ã/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ã/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ã (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ã (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ã=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ã=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ã;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ã;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ã\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ã\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ä/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ä/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ä (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ä (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ä=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ä=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ä;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ä;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ä\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ä\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Å/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Å/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Å (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Å (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Å=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Å=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Å;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Å;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Å\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Å\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Æ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Æ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Æ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Æ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Æ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Æ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Æ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Æ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Æ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Æ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ç/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ç/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ç (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ç (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ç=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ç=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ç;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ç;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ç\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ç\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"È/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"È/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/È (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/È (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;È=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;È=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=È;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"È\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=È;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"È\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"È\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"È\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"È\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"È\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"É/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"É/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/É (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/É (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;É=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;É=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=É;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"É\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=É;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"É\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"É\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"É\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"É\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"É\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ê/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ê/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ê (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ê (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ê=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ê=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ê;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ê;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ê\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ê\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ë/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ë/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ë (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ë (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ë=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ë=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ë;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ë;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ë\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ë\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ì/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ì/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ì (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ì (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ì=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ì=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ì;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ì;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ì\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ì\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Í/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Í/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Í (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Í (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Í=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Í=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Í;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Í;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Í\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Í\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Î/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Î/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Î (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Î (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Î=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Î=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Î;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Î;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Î\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Î\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ï/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ï/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ï (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ï (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ï=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ï=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ï;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ï;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ï\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ï\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ð/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ð/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ð (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ð (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ð=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ð=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ð;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ð;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ð\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ð\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ñ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ñ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ñ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ñ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ñ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ñ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ñ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ñ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ñ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ñ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ò/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ò/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ò (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ò (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ò=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ò=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ò;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ò;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ò\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ò\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ó/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ó/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ó (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ó (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ó=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ó=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ó;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ó;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ó\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ó\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ô/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ô/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ô (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ô (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ô=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ô=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ô;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ô;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ô\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ô\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Õ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Õ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Õ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Õ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Õ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Õ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Õ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Õ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Õ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Õ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ö/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ö/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ö (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ö (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ö=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ö=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ö;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ö;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ö\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ö\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"×/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"×/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/× (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/× (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;×=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;×=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=×;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"×\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=×;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"×\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"×\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"×\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"×\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"×\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ø/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ø/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ø (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ø (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ø=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ø=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ø;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ø;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ø\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ø\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ù/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ù/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ù (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ù (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ù=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ù=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ù;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ù;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ù\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ù\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ú/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ú/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ú (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ú (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ú=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ú=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ú;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ú;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ú\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ú\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Û/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Û/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Û (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Û (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Û=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Û=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Û;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Û;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Û\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Û\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ü/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ü/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ü (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ü (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ü=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ü=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ü;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ü;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ü\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ü\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Ý/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Ý/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ý (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Ý (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Ý=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Ý=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Ý;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Ý;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ý\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Ý\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"Þ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Þ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Þ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/Þ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;Þ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;Þ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=Þ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=Þ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Þ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"Þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"Þ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"Þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ß/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ß/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ß (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ß (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ß=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ß=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ß;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ß\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ß;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ß\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ß\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ß\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ß\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ß\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"à/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"à/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/à (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/à (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;à=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;à=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=à;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"à\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=à;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"à\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"à\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"à\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"à\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"à\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"á/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"á/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/á (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/á (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;á=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;á=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=á;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=á;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"á\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"á\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"á\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"â/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"â/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/â (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/â (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;â=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;â=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=â;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=â;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"â\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"â\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"â\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ã/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ã/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ã (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ã (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ã=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ã=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ã;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ã;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ã\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ã\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ã\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ä/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ä/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ä (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ä (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ä=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ä=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ä;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ä;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ä\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ä\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ä\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"å/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"å/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/å (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/å (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;å=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;å=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=å;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=å;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"å\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"å\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"å\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"æ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"æ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/æ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/æ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;æ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;æ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=æ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=æ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"æ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"æ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"æ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ç/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ç/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ç (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ç (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ç=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ç=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ç;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ç;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ç\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ç\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ç\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"è/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"è/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/è (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/è (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;è=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;è=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=è;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"è\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=è;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"è\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"è\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"è\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"è\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"è\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"é/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"é/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/é (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/é (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;é=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;é=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=é;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"é\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=é;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"é\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"é\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"é\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"é\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"é\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ê/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ê/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ê (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ê (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ê=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ê=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ê;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ê;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ê\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ê\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ê\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ë/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ë/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ë (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ë (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ë=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ë=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ë;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ë;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ë\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ë\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ë\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ì/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ì/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ì (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ì (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ì=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ì=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ì;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ì;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ì\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ì\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ì\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"í/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"í/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/í (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/í (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;í=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;í=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=í;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=í;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"í\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"í\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"í\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"î/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"î/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/î (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/î (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;î=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;î=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=î;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=î;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"î\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"î\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"î\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ï/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ï/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ï (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ï (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ï=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ï=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ï;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ï;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ï\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ï\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ï\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ð/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ð/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ð (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ð (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ð=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ð=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ð;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ð;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ð\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ð\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ð\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ñ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ñ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ñ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ñ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ñ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ñ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ñ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ñ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ñ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ñ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ñ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ò/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ò/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ò (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ò (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ò=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ò=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ò;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ò;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ò\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ò\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ò\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ó/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ó/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ó (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ó (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ó=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ó=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ó;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ó;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ó\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ó\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ó\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ô/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ô/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ô (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ô (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ô=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ô=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ô;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ô;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ô\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ô\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ô\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"õ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"õ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/õ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/õ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;õ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;õ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=õ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=õ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"õ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"õ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"õ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ö/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ö/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ö (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ö (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ö=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ö=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ö;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ö;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ö\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ö\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ö\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"÷/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"÷/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/÷ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/÷ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;÷=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;÷=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=÷;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"÷\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=÷;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"÷\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"÷\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"÷\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"÷\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"÷\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ø/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ø/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ø (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ø (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ø=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ø=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ø;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ø;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ø\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ø\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ø\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ù/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ù/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ù (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ù (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ù=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ù=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ù;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ù;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ù\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ù\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ù\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ú/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ú/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ú (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ú (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ú=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ú=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ú;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ú;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ú\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ú\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ú\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"û/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"û/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/û (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/û (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;û=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;û=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=û;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=û;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"û\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"û\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"û\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ü/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ü/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ü (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ü (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ü=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ü=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ü;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ü;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ü\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ü\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ü\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ý/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ý/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ý (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ý (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ý=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ý=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ý;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ý;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ý\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ý\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ý\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"þ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"þ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/þ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/þ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;þ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;þ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=þ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=þ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"þ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"þ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"þ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"ÿ/x (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"ÿ/x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ÿ (Blob/File)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/ÿ (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;ÿ=x;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;ÿ=x;bonus=x (Request/Response)\",\n            \"success\": true\n          },\n          {\n            \"name\": \"x/x;x=ÿ;bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ÿ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=ÿ;bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ÿ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ÿ\\\";bonus=x (Blob/File)\",\n            \"success\": false,\n            \"message\": \"assert_equals: Blob expected \\\"x/x;x=\\\\\\\"ÿ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          },\n          {\n            \"name\": \"x/x;x=\\\"ÿ\\\";bonus=x (Request/Response)\",\n            \"success\": false,\n            \"message\": \"assert_equals: expected \\\"x/x;x=\\\\\\\"ÿ\\\\\\\";bonus=x\\\" but got \\\"\\\"\"\n          }\n        ]\n      }\n    },\n    \"sniffing\": {\n      \"html.window.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"HTML is not sniffed for a \\\"feed\\\": atom\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          },\n          {\n            \"name\": \"HTML is not sniffed for a \\\"feed\\\": rss\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      }\n    }\n  },\n  \"websockets\": {\n    \"Close-1000-reason.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be opened expected true got false\"\n        }\n      ]\n    },\n    \"Close-1000-reason.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be opened expected true got false\"\n        }\n      ]\n    },\n    \"Close-1000-reason.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1000-verify-code.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - event.code == 1000 and event.reason = 'Clean Close'\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1000-verify-code.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - event.code == 1000 and event.reason = 'Clean Close'\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-1000-verify-code.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000, reason) - event.code == 1000 and event.reason = 'Clean Close'\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1000.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1000.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be opened expected true got false\"\n        }\n      ]\n    },\n    \"Close-1000.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1000) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1005-verify-code.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close() - return close code is 1005 - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1005-verify-code.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close() - return close code is 1005 - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-1005-verify-code.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close() - return close code is 1005 - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1005.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1005) - see '7.1.5.  The WebSocket Connection Close Code' in http://www.ietf.org/rfc/rfc6455.txt\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-1005.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1005) - see '7.1.5.  The WebSocket Connection Close Code' in http://www.ietf.org/rfc/rfc6455.txt\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        }\n      ]\n    },\n    \"Close-1005.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(1005) - see '7.1.5.  The WebSocket Connection Close Code' in http://www.ietf.org/rfc/rfc6455.txt\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-2999-reason.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(2999, reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-2999-reason.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(2999, reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        }\n      ]\n    },\n    \"Close-2999-reason.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(2999, reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-3000-reason.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-3000-reason.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-3000-reason.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-3000-verify-code.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - verify return code is 3000 - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-3000-verify-code.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - verify return code is 3000 - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-3000-verify-code.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(3000, reason) - verify return code is 3000 - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-4999-reason.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(4999, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-4999-reason.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(4999, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-4999-reason.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(4999, reason) - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-Reason-124Bytes.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-Reason-124Bytes.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        }\n      ]\n    },\n    \"Close-Reason-124Bytes.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(code, 'reason more than 123 bytes') - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-delayed.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close should not emit until handshake completes - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-delayed.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close should not emit until handshake completes - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-delayed.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close should not emit until handshake completes - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-onlyReason.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(only reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-onlyReason.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(only reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        }\n      ]\n    },\n    \"Close-onlyReason.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(only reason) - INVALID_ACCESS_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-readyState-Closed.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-readyState-Closed.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-readyState-Closed.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-readyState-Closing.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSING state just before onclose is called\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-readyState-Closing.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSING state just before onclose is called\",\n          \"success\": false,\n          \"message\": \"assert_true: open must be called expected true got false\"\n        }\n      ]\n    },\n    \"Close-readyState-Closing.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - readyState should be in CLOSING state just before onclose is called\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-reason-unpaired-surrogates.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-reason-unpaired-surrogates.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be opened expected true got false\"\n        }\n      ]\n    },\n    \"Close-reason-unpaired-surrogates.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Close the Connection - close(reason with unpaired surrogates) - connection should get closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-server-initiated-close.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Server initiated Close - Client sends back a CLOSE - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-server-initiated-close.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Server initiated Close - Client sends back a CLOSE - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Close-server-initiated-close.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Server initiated Close - Client sends back a CLOSE - readyState should be in CLOSED state and wasClean is TRUE - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-undefined.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Close-undefined\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Close-undefined.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Close-undefined\",\n          \"success\": false,\n          \"message\": \"assert_true: open event must fire expected true got false\"\n        }\n      ]\n    },\n    \"Close-undefined.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Close-undefined\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-asciiSep-protocol-string.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with an ascii separator character - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-asciiSep-protocol-string.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with an ascii separator character - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-asciiSep-protocol-string.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with an ascii separator character - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-blocked-port.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic check\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 0\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 7\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 9\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 11\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 13\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 15\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 17\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 19\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 20\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 21\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 22\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 23\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 25\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 37\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 42\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 43\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 53\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 69\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 77\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 79\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 87\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 95\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 101\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 102\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 103\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 104\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 109\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 110\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 111\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 113\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 115\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 117\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 119\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 123\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 135\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 137\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 139\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 143\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 161\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 179\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 389\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 427\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 465\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 512\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 513\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 514\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 515\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 526\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 530\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 531\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 532\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 540\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 548\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 554\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 556\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 563\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 587\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 601\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 636\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 989\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 990\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 993\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 995\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1719\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1720\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1723\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 2049\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 3659\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4045\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4190\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5060\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5061\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6000\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6566\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6665\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6666\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6667\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6668\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6669\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6679\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6697\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 10080\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-blocked-port.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic check\",\n          \"success\": false,\n          \"message\": \"assert_unreached: Reached unreachable code\"\n        },\n        {\n          \"name\": \"WebSocket blocked port test 0\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 7\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 9\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 11\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 13\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 15\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 17\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 19\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 20\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 21\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 22\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 23\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 25\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 37\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 42\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 43\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 53\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 69\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 77\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 79\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 87\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 95\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 101\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 102\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 103\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 104\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 109\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 110\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 111\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 113\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 115\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 117\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 119\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 123\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 135\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 137\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 139\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 143\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 161\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 179\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 389\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 427\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 465\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 512\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 513\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 514\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 515\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 526\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 530\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 531\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 532\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 540\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 548\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 554\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 556\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 563\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 587\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 601\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 636\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 989\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 990\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 993\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 995\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1719\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1720\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1723\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 2049\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 3659\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4045\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4190\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5060\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5061\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6000\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6566\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6665\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6666\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6667\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6668\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6669\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6679\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6697\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 10080\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-blocked-port.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic check\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 0\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 7\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 9\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 11\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 13\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 15\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 17\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 19\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 20\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 21\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 22\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 23\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 25\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 37\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 42\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 43\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 53\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 69\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 77\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 79\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 87\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 95\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 101\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 102\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 103\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 104\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 109\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 110\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 111\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 113\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 115\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 117\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 119\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 123\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 135\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 137\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 139\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 143\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 161\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 179\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 389\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 427\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 465\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 512\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 513\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 514\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 515\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 526\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 530\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 531\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 532\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 540\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 548\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 554\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 556\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 563\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 587\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 601\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 636\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 989\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 990\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 993\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 995\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1719\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1720\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 1723\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 2049\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 3659\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4045\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 4190\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5060\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 5061\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6000\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6566\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6665\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6666\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6667\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6668\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6669\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6679\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 6697\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket blocked port test 10080\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-extensions-empty.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-extensions-empty.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be closed expected true got false\"\n        }\n      ]\n    },\n    \"Create-extensions-empty.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.extensions should be set to '' after connection is established - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-http-urls.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"WebSocket: ensure both HTTP schemes are supported\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-invalid-urls.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"new WebSocket(\\\"ws://foo bar.com/\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"wss://foo bar.com/\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"ftp://web-platform.test:8000/\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"mailto:example@example.org\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"about:blank\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"http://web-platform.test:8000/#\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"http://web-platform.test:8000/#test\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        },\n        {\n          \"name\": \"new WebSocket(\\\"#test\\\") should throw a \\\"SyntaxError\\\" DOMException\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-non-absolute-url.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a non absolute URL: test\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Create WebSocket - Pass a non absolute URL: ?\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Create WebSocket - Pass a non absolute URL: null\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Create WebSocket - Pass a non absolute URL: 123\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-nonAscii-protocol-string.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with non-ascii values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-nonAscii-protocol-string.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with non-ascii values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-nonAscii-protocol-string.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with non-ascii values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-on-worker-shutdown.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"WebSocket created after a worker self.close()\",\n          \"success\": false,\n          \"message\": \"Worker is not defined\"\n        }\n      ]\n    },\n    \"Create-protocol-with-space.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with a space in it - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocol-with-space.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with a space in it - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocol-with-space.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string with a space in it - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated-case-insensitive.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated-case-insensitive.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated-case-insensitive.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-protocols-repeated.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values - SYNTAX_ERR is thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-url-with-space.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-url-with-space.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-url-with-space.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a URL with a space - SYNTAX_ERR should be thrown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-url-with-windows-1252-encoding.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"URL's percent-encoding is always in UTF-8 for WebSocket\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-array-protocols.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-array-protocols.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url-array-protocols.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-binaryType-blob.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-binaryType-blob.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url-binaryType-blob.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.binaryType should be set to 'blob' after connection is established - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-empty.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.protocol should be empty before connection is established\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-empty.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.protocol should be empty before connection is established\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-empty.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - wsocket.protocol should be empty before connection is established\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-setCorrectly.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-setCorrectly.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-setCorrectly.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-string.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-string.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url-protocol-string.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url-protocol.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url-protocol.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL and a protocol string - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Create-valid-url.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Create-valid-url.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - Pass a valid URL - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-0byte-data.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 0 byte data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-0byte-data.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 0 byte data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-0byte-data.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 0 byte data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-65K-data.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-65K-data.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-65K-data.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-before-open.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket before connection is opened - INVALID_STATE_ERR is returned\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-before-open.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket before connection is opened - INVALID_STATE_ERR is returned\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-before-open.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket before connection is opened - INVALID_STATE_ERR is returned\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-65K-arraybuffer.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-65K-arraybuffer.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-65K-arraybuffer.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send 65K binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybuffer.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybuffer.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybuffer.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBuffer - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float16.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float16Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"Float16Array is not defined\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float16.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float16Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float16.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float16Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"Float16Array is not defined\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float32.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float32.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float32.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float32Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float64.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float64.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-float64.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Float64Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int16-offset.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int16-offset.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int16Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int32.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int32.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int32.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int32Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int8.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int8.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-int8.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Int8Array - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint16-offset-length.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint16-offset-length.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint16-offset-length.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint16Array with offset and length - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint32-offset.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint32-offset.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint32Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset-length.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset-length.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset-length.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset and length - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-arraybufferview-uint8-offset.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - ArrayBufferView - Uint8Array with offset - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-blob.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - Blob - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-binary-blob.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - Blob - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-binary-blob.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send binary data on a WebSocket - Blob - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-data.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-data.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: message should be received expected true got false\"\n        }\n      ]\n    },\n    \"Send-data.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-null.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send null data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-null.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send null data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-null.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send null data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-paired-surrogates.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send paired surrogates data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-paired-surrogates.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send paired surrogates data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-paired-surrogates.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send paired surrogates data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-unicode-data.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unicode data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-unicode-data.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unicode data on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-unicode-data.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unicode data on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-unpaired-surrogates.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unpaired surrogates on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"Send-unpaired-surrogates.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unpaired surrogates on a WebSocket - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: WebSocket connection should be open expected true got false\"\n        }\n      ]\n    },\n    \"Send-unpaired-surrogates.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Send unpaired surrogates on a WebSocket - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"back-forward-cache-with-closed-websocket-connection-ccns.tentative.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"back-forward-cache-with-closed-websocket-connection-ccns\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n        }\n      ]\n    },\n    \"back-forward-cache-with-closed-websocket-connection.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"back-forward-cache-with-closed-websocket-connection\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n        }\n      ]\n    },\n    \"back-forward-cache-with-open-websocket-connection-ccns.tentative.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"back-forward-cache-with-open-websocket-connection-ccns\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n        }\n      ]\n    },\n    \"back-forward-cache-with-open-websocket-connection.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"back-forward-cache-with-open-websocket-connection\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"TypeError: window.open is not a function\\\"\"\n        }\n      ]\n    },\n    \"basic-auth.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"HTTP basic authentication should work with WebSockets\",\n          \"success\": false,\n          \"message\": \"assert_unreached: open should succeed Reached unreachable code\"\n        }\n      ]\n    },\n    \"basic-auth.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"HTTP basic authentication should work with WebSockets\",\n          \"success\": true\n        }\n      ]\n    },\n    \"binary\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"001.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n          }\n        ]\n      },\n      \"001.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n          }\n        ]\n      },\n      \"002.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"004.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": true\n          }\n        ]\n      },\n      \"004.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": false,\n            \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n          }\n        ]\n      },\n      \"004.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": true\n          }\n        ]\n      },\n      \"005.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": true\n          }\n        ]\n      },\n      \"005.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": false,\n            \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n          }\n        ]\n      },\n      \"005.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"binaryType-wrong-value.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"binaryType-wrong-value.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be closed\",\n          \"success\": false,\n          \"message\": \"assert_true: connection should be opened expected true got false\"\n        }\n      ]\n    },\n    \"binaryType-wrong-value.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Create WebSocket - set binaryType to something other than blob or arraybuffer - SYNTAX_ERR is returned - Connection should be closed\",\n          \"success\": true\n        }\n      ]\n    },\n    \"bufferedAmount-unchanged-by-sync-xhr.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"bufferedAmount should not be updated during a sync XHR\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"bufferedAmount-unchanged-by-sync-xhr.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"bufferedAmount should not be updated during a sync XHR\",\n          \"success\": false,\n          \"message\": \"assert_unreached: open should succeed Reached unreachable code\"\n        }\n      ]\n    },\n    \"bufferedAmount-unchanged-by-sync-xhr.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"bufferedAmount should not be updated during a sync XHR\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"close-invalid.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"0 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"500 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"NaN on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"String on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"null on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"2**16+1000 on a websocket\",\n          \"success\": true\n        }\n      ]\n    },\n    \"close-invalid.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"0 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"500 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"NaN on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"String on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"null on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"2**16+1000 on a websocket\",\n          \"success\": true\n        }\n      ]\n    },\n    \"close-invalid.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"0 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"500 on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"NaN on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"String on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"null on a websocket\",\n          \"success\": true\n        },\n        {\n          \"name\": \"2**16+1000 on a websocket\",\n          \"success\": true\n        }\n      ]\n    },\n    \"closing-handshake\": {\n      \"002.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Reached unreachable code\"\n          }\n        ]\n      },\n      \"002.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"003.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"003\",\n            \"success\": true\n          }\n        ]\n      },\n      \"003.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"003\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Reached unreachable code\"\n          }\n        ]\n      },\n      \"003.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"003\",\n            \"success\": true\n          }\n        ]\n      },\n      \"004.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": true\n          }\n        ]\n      },\n      \"004.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Reached unreachable code\"\n          }\n        ]\n      },\n      \"004.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"004\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"constructor.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Calling the WebSocket constructor with too many arguments should not throw.\",\n          \"success\": true\n        }\n      ]\n    },\n    \"constructor.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Calling the WebSocket constructor with too many arguments should not throw.\",\n          \"success\": true\n        }\n      ]\n    },\n    \"constructor.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Calling the WebSocket constructor with too many arguments should not throw.\",\n          \"success\": true\n        }\n      ]\n    },\n    \"cookies\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"001.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"001.html?wss&wpt_flags=https\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"002.html?default\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"002.html?wpt_flags=h2\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"assert_unreached: error Reached unreachable code\"\n          }\n        ]\n      },\n      \"002.html?wss&wpt_flags=https\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"003.html?default\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"003.html?wpt_flags=h2\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"003.html?wss&wpt_flags=https\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"004.html?default\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"004.html?wss&wpt_flags=https\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"005.html?default\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"005.html?wss&wpt_flags=https\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"006.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"006\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"006.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"006\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"006.html?wss&wpt_flags=https\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"006\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"007.html?default\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"007\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"007.html?wpt_flags=h2\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"007\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"007.html?wss&wpt_flags=https\": {\n        \"success\": false,\n        \"cases\": [\n          {\n            \"name\": \"007\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"cross-origin-cookie-set.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Cookie should be set\",\n            \"success\": false,\n            \"message\": \"assert_true: expected true got false\"\n          }\n        ]\n      },\n      \"third-party-cookie-accepted.https.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Test that third-party cookies are accepted for WebSockets.\",\n            \"success\": false,\n            \"message\": \"assert_not_equals: request should contain cookies. got disallowed value \\\"(none)\\\"\"\n          }\n        ]\n      }\n    },\n    \"eventhandlers.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Event handler for open should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for error should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for close should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for message should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventhandlers.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Event handler for open should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for error should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for close should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for message should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventhandlers.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Event handler for open should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for error should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for close should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Event handler for message should have [TreatNonCallableAsNull]\",\n          \"success\": true\n        }\n      ]\n    },\n    \"extended-payload-length.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Application data is 125 byte which means any 'Extended payload length' field isn't used at all.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is 126 byte which starts to use the 16 bit 'Extended payload length' field.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is 0xFFFF byte which means the upper bound of the 16 bit 'Extended payload length' field.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is (0xFFFF + 1) byte which starts to use the 64 bit 'Extended payload length' field\",\n          \"success\": true\n        }\n      ]\n    },\n    \"extended-payload-length.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Application data is 125 byte which means any 'Extended payload length' field isn't used at all.\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        },\n        {\n          \"name\": \"Application data is 126 byte which starts to use the 16 bit 'Extended payload length' field.\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        },\n        {\n          \"name\": \"Application data is 0xFFFF byte which means the upper bound of the 16 bit 'Extended payload length' field.\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        },\n        {\n          \"name\": \"Application data is (0xFFFF + 1) byte which starts to use the 64 bit 'Extended payload length' field\",\n          \"success\": false,\n          \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n        }\n      ]\n    },\n    \"extended-payload-length.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Application data is 125 byte which means any 'Extended payload length' field isn't used at all.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is 126 byte which starts to use the 16 bit 'Extended payload length' field.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is 0xFFFF byte which means the upper bound of the 16 bit 'Extended payload length' field.\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Application data is (0xFFFF + 1) byte which starts to use the 64 bit 'Extended payload length' field\",\n          \"success\": true\n        }\n      ]\n    },\n    \"idlharness.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"idl_test setup\",\n          \"success\": true\n        },\n        {\n          \"name\": \"idl_test validation\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: existence and properties of interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface object length\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface object name\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: existence and properties of interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute url\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CONNECTING on interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CONNECTING on interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant OPEN on interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant OPEN on interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CLOSING on interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CLOSING on interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CLOSED on interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: constant CLOSED on interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute readyState\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute bufferedAmount\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute onopen\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute onerror\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute onclose\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute extensions\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute protocol\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: operation close(optional unsigned short, optional USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute onmessage\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: attribute binaryType\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: operation send((BufferSource or Blob or USVString))\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket must be primary interface of new WebSocket(\\\"ws://invalid\\\")\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Stringification of new WebSocket(\\\"ws://invalid\\\")\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"url\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"CONNECTING\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"OPEN\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"CLOSING\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"CLOSED\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"readyState\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"bufferedAmount\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"onopen\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"onerror\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"onclose\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"extensions\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"protocol\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"close(optional unsigned short, optional USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: calling close(optional unsigned short, optional USVString) on new WebSocket(\\\"ws://invalid\\\") with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"onmessage\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"binaryType\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: new WebSocket(\\\"ws://invalid\\\") must inherit property \\\"send((BufferSource or Blob or USVString))\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"WebSocket interface: calling send((BufferSource or Blob or USVString)) on new WebSocket(\\\"ws://invalid\\\") with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: existence and properties of interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface object length\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface object name\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: existence and properties of interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: attribute wasClean\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: attribute code\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: attribute reason\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent must be primary interface of new CloseEvent(\\\"close\\\")\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Stringification of new CloseEvent(\\\"close\\\")\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: new CloseEvent(\\\"close\\\") must inherit property \\\"wasClean\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: new CloseEvent(\\\"close\\\") must inherit property \\\"code\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"CloseEvent interface: new CloseEvent(\\\"close\\\") must inherit property \\\"reason\\\" with the proper type\",\n          \"success\": true\n        }\n      ]\n    },\n    \"interfaces\": {\n      \"CloseEvent\": {\n        \"clean-close.html?default\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean-close\",\n              \"success\": true\n            }\n          ]\n        },\n        \"clean-close.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean-close\",\n              \"success\": false,\n              \"message\": \"assert_equals: expected true but got false\"\n            }\n          ]\n        },\n        \"clean-close.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean-close\",\n              \"success\": true\n            }\n          ]\n        },\n        \"constructor.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"new CloseEvent() without dictionary\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new CloseEvent() with dictionary\",\n              \"success\": true\n            }\n          ]\n        },\n        \"historical.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"initCloseEvent\",\n              \"success\": true\n            }\n          ]\n        }\n      },\n      \"WebSocket\": {\n        \"bufferedAmount\": {\n          \"bufferedAmount-arraybuffer.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-arraybuffer\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-arraybuffer.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-arraybuffer\",\n                \"success\": false,\n                \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n              }\n            ]\n          },\n          \"bufferedAmount-arraybuffer.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-arraybuffer\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-blob.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-blob\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-blob.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-blob\",\n                \"success\": false,\n                \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n              }\n            ]\n          },\n          \"bufferedAmount-blob.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-blob\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-defineProperty-getter.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-defineProperty-getter\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-defineProperty-getter.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-defineProperty-getter\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-defineProperty-setter.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-defineProperty-setter\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-defineProperty-setter.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-defineProperty-setter\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-deleting.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-deleting\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-deleting.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-deleting\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-getting.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-getting\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-getting.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-getting\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"bufferedAmount-getting.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-getting\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-initial.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-initial\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-initial.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-initial\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-large.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-large\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-large.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-large\",\n                \"success\": false,\n                \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n              }\n            ]\n          },\n          \"bufferedAmount-large.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-large\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-readonly.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-readonly\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-readonly.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-readonly\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-unicode.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-unicode\",\n                \"success\": true\n              }\n            ]\n          },\n          \"bufferedAmount-unicode.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-unicode\",\n                \"success\": false,\n                \"message\": \"assert_unreached: close event should not fire Reached unreachable code\"\n              }\n            ]\n          },\n          \"bufferedAmount-unicode.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"bufferedAmount-unicode\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"close\": {\n          \"close-basic.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-basic\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-basic.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-basic\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-connecting-async.any.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close event should be fired asynchronously when WebSocket is connecting\",\n                \"success\": false,\n                \"message\": \"assert_true: ws.close() should have returned expected true got false\"\n              }\n            ]\n          },\n          \"close-connecting-async.any.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close event should be fired asynchronously when WebSocket is connecting\",\n                \"success\": false,\n                \"message\": \"assert_true: ws.close() should have returned expected true got false\"\n              }\n            ]\n          },\n          \"close-connecting-async.any.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close event should be fired asynchronously when WebSocket is connecting\",\n                \"success\": false,\n                \"message\": \"assert_true: ws.close() should have returned expected true got false\"\n              }\n            ]\n          },\n          \"close-connecting.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-connecting\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"close-connecting.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-connecting\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"close-multiple.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-multiple\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-multiple.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-multiple\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-nested.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-nested\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-nested.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-nested\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-replace.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-replace\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-replace.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-replace\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-return.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-return\",\n                \"success\": true\n              }\n            ]\n          },\n          \"close-return.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"close-return\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"constants\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Constants on constructors CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Constants on constructors CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Constants on constructors CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Readonly constants CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Readonly constants CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Readonly constants CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"003 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"WebSocket.prototype.CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.CLOSED\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"WebSocket.prototype.CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"WebSocket.prototype.CLOSED\",\n                \"success\": true\n              },\n              {\n                \"name\": \"ws.CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"defineProperty getter CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"defineProperty getter CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty getter CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"defineProperty setter CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter CLOSED\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"defineProperty setter CONNECTING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter OPEN\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter CLOSING\",\n                \"success\": true\n              },\n              {\n                \"name\": \"defineProperty setter CLOSED\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"events\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"001 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"002 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"003.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"004.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"007.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"007.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"007.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"008.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"008.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"009.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"009\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"009.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"009\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"010.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"010\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"010.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"010\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 1\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 2\",\n                \"success\": true\n              },\n              {\n                \"name\": \"010 3\",\n                \"success\": true\n              }\n            ]\n          },\n          \"011.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"011.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"012.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"012.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"013.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"013.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"014.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Setting event handlers to null onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"014.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Setting event handlers to null onclose\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onopen\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onerror\",\n                \"success\": true\n              },\n              {\n                \"name\": \"Setting event handlers to null onmessage\",\n                \"success\": true\n              }\n            ]\n          },\n          \"015.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"015\",\n                \"success\": false,\n                \"message\": \"assert_true: expected true got false\"\n              }\n            ]\n          },\n          \"015.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"015\",\n                \"success\": true\n              }\n            ]\n          },\n          \"015.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"015\",\n                \"success\": false,\n                \"message\": \"assert_true: expected true got false\"\n              }\n            ]\n          },\n          \"016.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"016\",\n                \"success\": true\n              }\n            ]\n          },\n          \"016.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"016\",\n                \"success\": false,\n                \"message\": \"assert_equals: expected 7 but got 1\"\n              }\n            ]\n          },\n          \"016.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"016\",\n                \"success\": true\n              }\n            ]\n          },\n          \"017.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"017\",\n                \"success\": true\n              }\n            ]\n          },\n          \"017.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"017\",\n                \"success\": true\n              }\n            ]\n          },\n          \"017.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"017\",\n                \"success\": true\n              }\n            ]\n          },\n          \"018.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"open, message, error and close events\",\n                \"success\": false,\n                \"message\": \"assert_equals: error e.toString() expected \\\"[object Event]\\\" but got \\\"[object ErrorEvent]\\\"\"\n              }\n            ]\n          },\n          \"018.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"open, message, error and close events\",\n                \"success\": false,\n                \"message\": \"assert_equals: error e.toString() expected \\\"[object Event]\\\" but got \\\"[object ErrorEvent]\\\"\"\n              }\n            ]\n          },\n          \"018.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"open, message, error and close events\",\n                \"success\": false,\n                \"message\": \"assert_equals: error e.toString() expected \\\"[object Event]\\\" but got \\\"[object ErrorEvent]\\\"\"\n              }\n            ]\n          },\n          \"019.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"019\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 1\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 2\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 3\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"019.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"019\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 1\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 2\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              },\n              {\n                \"name\": \"019 3\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"020.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"020\",\n                \"success\": true\n              }\n            ]\n          },\n          \"020.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"020\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"extensions\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"protocol\": {\n          \"protocol-initial.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"protocol-initial\",\n                \"success\": true\n              }\n            ]\n          },\n          \"protocol-initial.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"protocol-initial\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"readyState\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"007.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": true\n              }\n            ]\n          },\n          \"007.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": true\n              }\n            ]\n          },\n          \"008.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": true\n              }\n            ]\n          },\n          \"008.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"send\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"lone low surrogate\",\n                \"success\": true\n              },\n              {\n                \"name\": \"lone high surrogate\",\n                \"success\": true\n              },\n              {\n                \"name\": \"surrogates in wrong order\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"lone low surrogate\",\n                \"success\": true\n              },\n              {\n                \"name\": \"lone high surrogate\",\n                \"success\": true\n              },\n              {\n                \"name\": \"surrogates in wrong order\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": false,\n                \"message\": \"assert_unreached: close event should not fire before message event Reached unreachable code\"\n              }\n            ]\n          },\n          \"006.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"007.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": true\n              }\n            ]\n          },\n          \"007.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"007.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"007\",\n                \"success\": true\n              }\n            ]\n          },\n          \"008.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": true\n              }\n            ]\n          },\n          \"008.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"008\",\n                \"success\": true\n              }\n            ]\n          },\n          \"009.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"009\",\n                \"success\": true\n              }\n            ]\n          },\n          \"009.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"009\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"009.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"009\",\n                \"success\": true\n              }\n            ]\n          },\n          \"010.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Constructor succeeds\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"010.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"Constructor succeeds\",\n                \"success\": false,\n                \"message\": \"document is not defined\"\n              }\n            ]\n          },\n          \"011.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"011\",\n                \"success\": true\n              }\n            ]\n          },\n          \"011.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"011\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"011.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"011\",\n                \"success\": true\n              }\n            ]\n          },\n          \"012.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"012\",\n                \"success\": true\n              }\n            ]\n          },\n          \"012.html?wpt_flags=h2\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"012\",\n                \"success\": false,\n                \"message\": \"assert_unreached: Reached unreachable code\"\n              }\n            ]\n          },\n          \"012.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"012\",\n                \"success\": true\n              }\n            ]\n          }\n        },\n        \"url\": {\n          \"001.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"001.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"001\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"002.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"002\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"003.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"003\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"004.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"004\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"005.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"005\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"006.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"006\",\n                \"success\": true\n              }\n            ]\n          },\n          \"resolve.html?default\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"resolve\",\n                \"success\": true\n              }\n            ]\n          },\n          \"resolve.html?wss\": {\n            \"success\": true,\n            \"cases\": [\n              {\n                \"name\": \"resolve\",\n                \"success\": true\n              }\n            ]\n          }\n        }\n      }\n    },\n    \"keeping-connection-open\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"001.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Reached unreachable code\"\n          }\n        ]\n      },\n      \"001.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"mixed-content.https.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"constructing an insecure WebSocket in a secure context should throw\",\n          \"success\": false,\n          \"message\": \"assert_throws_dom: constructor should throw function \\\"() => CreateInsecureWebSocket()\\\" did not throw\"\n        }\n      ]\n    },\n    \"multi-globals\": {\n      \"message-received.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"url-parsing\": {\n        \"url-parsing.html\": {\n          \"success\": false,\n          \"cases\": []\n        }\n      }\n    },\n    \"opening-handshake\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"001.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"001.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"assert_unreached: Reached unreachable code\"\n          }\n        ]\n      },\n      \"002.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": true\n          }\n        ]\n      },\n      \"003.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"003\",\n            \"success\": true\n          }\n        ]\n      },\n      \"003.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"003\",\n            \"success\": true\n          }\n        ]\n      },\n      \"005.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": true\n          }\n        ]\n      },\n      \"005.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": true\n          }\n        ]\n      },\n      \"006.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"006\",\n            \"success\": true\n          }\n        ]\n      },\n      \"006.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"006\",\n            \"success\": true\n          }\n        ]\n      },\n      \"received-301-code.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Websockets - Received 301 code\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"referrer.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Ensure no Referer header is included\",\n          \"success\": true\n        }\n      ]\n    },\n    \"remove-own-iframe-during-onerror.window.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"removing an iframe from within an onerror handler should work\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"remove-own-iframe-during-onerror.window.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"removing an iframe from within an onerror handler should work\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"remove-own-iframe-during-onerror.window.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"removing an iframe from within an onerror handler should work\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"security\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"001.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": true\n          }\n        ]\n      },\n      \"002.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      },\n      \"002.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"XMLHttpRequest is not defined\"\n          }\n        ]\n      }\n    },\n    \"send-many-64K-messages-with-backpressure.any.html?default\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sending 50 messages of size 65536 with backpressure applied should not hang\",\n          \"success\": true\n        }\n      ]\n    },\n    \"send-many-64K-messages-with-backpressure.any.html?wpt_flags=h2\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sending 50 messages of size 65536 with backpressure applied should not hang\",\n          \"success\": false,\n          \"message\": \"assert_true: connection should have been opened expected true got false\"\n        }\n      ]\n    },\n    \"send-many-64K-messages-with-backpressure.any.html?wss\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sending 50 messages of size 65536 with backpressure applied should not hang\",\n          \"success\": true\n        }\n      ]\n    },\n    \"stream\": {\n      \"tentative\": {\n        \"abort.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"abort before constructing should prevent connection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"abort during handshake should work\",\n              \"success\": false,\n              \"message\": \"promise_rejects_dom: opened should reject function \\\"function() { throw e; }\\\" threw object \\\"WebSocketError: Socket never opened\\\" that is not a DOMException AbortError: property \\\"code\\\" is equal to 0, expected 20\"\n            },\n            {\n              \"name\": \"abort after connect should do nothing\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"abort.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"abort before constructing should prevent connection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"abort during handshake should work\",\n              \"success\": false,\n              \"message\": \"promise_rejects_dom: opened should reject function \\\"function() { throw e; }\\\" threw object \\\"WebSocketError: Socket never opened\\\" that is not a DOMException AbortError: property \\\"code\\\" is equal to 0, expected 20\"\n            },\n            {\n              \"name\": \"abort after connect should do nothing\",\n              \"success\": true\n            }\n          ]\n        },\n        \"backpressure-receive.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"backpressure should be applied to received messages\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"backpressure-receive.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"backpressure should be applied to received messages\",\n              \"success\": false,\n              \"message\": \"assert_greater_than_equal: data send should have taken at least 2 seconds expected a number greater than or equal to 1.8 but got 0.03000044822692871\"\n            }\n          ]\n        },\n        \"backpressure-send.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"backpressure should be applied to sent messages\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"backpressure-send.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"backpressure should be applied to sent messages\",\n              \"success\": false,\n              \"message\": \"assert_greater_than_equal: expected a number greater than or equal to 1800 but got 25.788400000000024\"\n            }\n          ]\n        },\n        \"close.any.html?default\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"close.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"close code should be sent to server and reflected back\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"no close argument should send empty Close frame\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"unspecified close code should send empty Close frame\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"unspecified close code with empty reason should send empty Close frame\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"unspecified close code with non-empty reason should set code to 1000\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close(true) should throw a TypeError\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close() with an overlong reason should throw\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close during handshake should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"close() with invalid code 999 should throw\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close() with invalid code 1001 should throw\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close() with invalid code 2999 should throw\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close() with invalid code 5000 should throw\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"closing the writable should result in a clean close\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"writer close() promise should not resolve until handshake completes\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"incomplete closing handshake should be considered unclean close\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable should result in a clean close\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable with attributes not wrapped in a WebSocketError should be ignored\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable with a code should send that code\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable with a code and reason should use them\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable with a reason but no code should default the close code\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"aborting the writable with a DOMException not set code or reason\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable should result in a clean close\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable with attributes not wrapped in a WebSocketError should be ignored\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable with a code should send that code\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable with a code and reason should use them\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable with a reason but no code should default the close code\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"canceling the readable with a DOMException not set code or reason\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"close.any.html?wss\": {\n          \"success\": false,\n          \"cases\": []\n        },\n        \"constructor.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"constructing with no URL should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with an invalid URL should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with invalid options should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"protocols should be required to be a list\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with a valid URL should work\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"setting a protocol in the constructor should work\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"connection failure should reject the promises\",\n              \"success\": true\n            },\n            {\n              \"name\": \"wss.opened should resolve to the right types\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"constructor.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"constructing with no URL should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with an invalid URL should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with invalid options should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"protocols should be required to be a list\",\n              \"success\": true\n            },\n            {\n              \"name\": \"constructing with a valid URL should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"setting a protocol in the constructor should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"connection failure should reject the promises\",\n              \"success\": true\n            },\n            {\n              \"name\": \"wss.opened should resolve to the right types\",\n              \"success\": false,\n              \"message\": \"assert_equals: extensions should be a string expected \\\"string\\\" but got \\\"object\\\"\"\n            }\n          ]\n        },\n        \"remote-close.any.html?default\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean close should be clean\",\n              \"flaky\": true\n            },\n            {\n              \"name\": \"close frame with no body should result in status code 1005\",\n              \"success\": true\n            },\n            {\n              \"name\": \"reason should be passed through\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 reason should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"close with unwritten data should not be considered clean\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"remote code and reason should be used\",\n              \"success\": true\n            },\n            {\n              \"name\": \"abrupt close should give an error\",\n              \"success\": true\n            }\n          ]\n        },\n        \"remote-close.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean close should be clean\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"close frame with no body should result in status code 1005\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: unclean close\\\"\"\n            },\n            {\n              \"name\": \"reason should be passed through\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: unclean close\\\"\"\n            },\n            {\n              \"name\": \"UTF-8 reason should work\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: unclean close\\\"\"\n            },\n            {\n              \"name\": \"close with unwritten data should not be considered clean\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"remote code and reason should be used\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"abrupt close should give an error\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"remote-close.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"clean close should be clean\",\n              \"success\": true\n            },\n            {\n              \"name\": \"close frame with no body should result in status code 1005\",\n              \"success\": true\n            },\n            {\n              \"name\": \"reason should be passed through\",\n              \"success\": true\n            },\n            {\n              \"name\": \"UTF-8 reason should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"close with unwritten data should not be considered clean\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"remote code and reason should be used\",\n              \"success\": true\n            },\n            {\n              \"name\": \"abrupt close should give an error\",\n              \"success\": true\n            }\n          ]\n        },\n        \"websocket-error.any.html\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"WebSocketError defaults should be correct\",\n              \"success\": true\n            },\n            {\n              \"name\": \"WebSocketError should be initialised from arguments\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new WebSocketError with invalid code 999 should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new WebSocketError with invalid code 1001 should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new WebSocketError with invalid code 2999 should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"new WebSocketError with invalid code 5000 should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"passing only close code to WebSocketError should work\",\n              \"success\": true\n            },\n            {\n              \"name\": \"passing a non-empty reason should cause the close code to be set to 1000\",\n              \"success\": true\n            },\n            {\n              \"name\": \"overlong reason should throw\",\n              \"success\": true\n            },\n            {\n              \"name\": \"reason should be rejected based on utf-8 bytes, not character count\",\n              \"success\": true\n            }\n          ]\n        },\n        \"write.any.html?default\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"a write that was incomplete at close time should reject\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed promise should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"garbage collection after close with a pending write promise should not crash\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed promise should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"writing a value that cannot be stringified should cause a rejection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"writing a resizable ArrayBuffer should be rejected\",\n              \"success\": true\n            },\n            {\n              \"name\": \"writing a view on a shared buffer should be rejected\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Garbage collecting a WebSocket stream doesn't crash while write promise is pending\",\n              \"success\": true\n            }\n          ]\n        },\n        \"write.any.html?wpt_flags=h2\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"a write that was incomplete at close time should reject\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"garbage collection after close with a pending write promise should not crash\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"writing a value that cannot be stringified should cause a rejection\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"writing a resizable ArrayBuffer should be rejected\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"writing a view on a shared buffer should be rejected\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            },\n            {\n              \"name\": \"Garbage collecting a WebSocket stream doesn't crash while write promise is pending\",\n              \"success\": false,\n              \"message\": \"promise_test: Unhandled rejection with value: object \\\"WebSocketError: Socket never opened\\\"\"\n            }\n          ]\n        },\n        \"write.any.html?wss\": {\n          \"success\": true,\n          \"cases\": [\n            {\n              \"name\": \"a write that was incomplete at close time should reject\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed promise should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"garbage collection after close with a pending write promise should not crash\",\n              \"success\": false,\n              \"message\": \"assert_unreached: closed promise should reject Reached unreachable code\"\n            },\n            {\n              \"name\": \"writing a value that cannot be stringified should cause a rejection\",\n              \"success\": true\n            },\n            {\n              \"name\": \"writing a resizable ArrayBuffer should be rejected\",\n              \"success\": true\n            },\n            {\n              \"name\": \"writing a view on a shared buffer should be rejected\",\n              \"success\": true\n            },\n            {\n              \"name\": \"Garbage collecting a WebSocket stream doesn't crash while write promise is pending\",\n              \"success\": true\n            }\n          ]\n        }\n      }\n    },\n    \"unload-a-document\": {\n      \"001.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"001.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"001.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"001\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"002.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"002.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"002.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"002\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"003.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"004.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"005.html?default\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"005.html?wpt_flags=h2\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      },\n      \"005.html?wss\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"005\",\n            \"success\": false,\n            \"message\": \"window.open is not a function\"\n          }\n        ]\n      }\n    }\n  },\n  \"xhr\": {\n    \"XMLHttpRequest-withCredentials.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"default value is false, set value is true\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"can also be set in OPEN state\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"setting on synchronous XHR\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"setting withCredentials when not in UNSENT, OPENED state (asynchronous)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"setting withCredentials when in DONE state (synchronous)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-after-receive.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-after-receive\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-after-send.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-after-send\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-after-stop.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"abort-after-timeout.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-after-timeout\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-during-done.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"abort-during-headers-received.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-during-headers-received\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-during-loading.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-during-loading\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-during-open.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: abort() during OPEN\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-during-readystatechange.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"abort-during-unsent.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-during-unsent\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-during-upload.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-during-upload\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-event-abort.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-event-abort\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-event-listeners.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-event-listeners\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-event-loadend.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-event-loadend\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-upload-event-abort.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: The abort() method: Fire a progress event named abort on the XMLHttpRequestUpload object\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-upload-event-loadend.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: The abort() method: Fire a progress event named loadend on the XMLHttpRequestUpload object\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"abort-with-error.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"abort-with-error\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-and-redirects-async-same-origin.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Request without credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=* (with star)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request with credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=* (with star)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin (no credentials)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin (no credentials)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-and-redirects-async.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Request is redirected without CORS headers to a response with Access-Control-Allow-Origin=*\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request is redirected to a response with Access-Control-Allow-Origin=*\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request with user info is redirected to a response with Access-Control-Allow-Origin=*\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request is redirect to a bad URL\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Preflighted request is redirected to a response with Access-Control-Allow-Origin=*\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Preflighted request is redirected to a response with Access-Control-Allow-Origin=* and header allowed\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Request is redirected to a same-origin resource file\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-and-redirects.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Local sync redirect to remote origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Local async redirect to remote origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Remote sync redirect to local origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Remote async redirect to local origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Remote sync redirect to same remote origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Remote async redirect to same remote origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-access-control-origin-header.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Access control test with origin header\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-async.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic async cross-origin XHR request\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-non-cors-safelisted-method-async.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Allow async PUT request\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-non-cors-safelisted-method.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Allow PUT request\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-preflight-cache-invalidation-by-header.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"access-control-basic-allow-preflight-cache-invalidation-by-method.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"access-control-basic-allow-preflight-cache-timeout.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"access-control-basic-allow-preflight-cache.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Preflight cache should allow second request\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-basic-allow-star.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"access-control-basic-allow.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Allow basic\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-expose-headers-on-redirect.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"access-control-expose-headers-on-redirect\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-preflight-request-allow-headers-returns-star.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"access-control-preflight-request-allow-headers-returns-star\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"access-control-preflight-request-header-returns-origin.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"access-control-preflight-request-header-returns-origin\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"blob-range.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"A simple blob range request.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"A blob range request with no type.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"A blob range request with no end.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"A blob range request with no start.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"A simple blob range request with whitespace.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob content with short content and a large range end\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob content with short content and a range end matching content length\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob range with whitespace before and after hyphen\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob range with whitespace after hyphen\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob range with whitespace around equals sign\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Blob range with no value\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range with incorrect range header\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range with incorrect range header #2\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range with incorrect range header #3\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range request with multiple range values\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range request with multiple range values and whitespace\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range request with trailing comma\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range with no start or end\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range request with short range end\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range start should be an ASCII digit\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range should have a dash\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range end should be an ASCII digit\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range should include '-'\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range should include '='\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob range should include 'bytes='\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob content with short content and a large range start\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Blob content with short content and a range start matching the content length\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"close-worker-with-xhr-in-progress.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Terminating a worker with a XHR in progress doesn't crash\",\n          \"success\": false,\n          \"message\": \"Worker is not defined\"\n        }\n      ]\n    },\n    \"content-type-unmodified.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"content-type-unmodified\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"cookies.http.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic non-cross-site cookie handling in XHR\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        }\n      ]\n    },\n    \"cors-expose-star.sub.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Basic Access-Control-Expose-Headers: * support\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"* for credentialed fetches only matches literally\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"* can be one of several values\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"cors-upload.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"event-abort.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-abort\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-error-order.sub.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-error-order\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-error.sub.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"onerror should be called\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"error while reading body should report zeros for loaded and total\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-load.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-load\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-loadend.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-loadend\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-loadstart-upload.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-loadstart-upload\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-loadstart.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-loadstart\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-progress.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-progress\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-readystate-sync-open.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: open() call fires sync readystate event (sync)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"XMLHttpRequest: open() call fires sync readystate event (async)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-readystatechange-loaded.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-readystatechange-loaded\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-timeout-order.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-timeout-order\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-timeout.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-timeout\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-upload-progress-crossorigin.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Upload events registered on time (http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered on time (resources/redirect.py?code=307&location=http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered too late (http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered too late (resources/redirect.py?code=307&location=http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"event-upload-progress.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Upload events registered on time (http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered on time (resources/redirect.py?code=307&location=http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered too late (http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Upload events registered too late (resources/redirect.py?code=307&location=http://www1.web-platform.test:8000/xhr/resources/corsenabled.py)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"firing-events-http-content-length.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"firing-events-http-content-length\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"firing-events-http-no-content-length.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"firing-events-http-no-content-length\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"formdata\": {\n      \"append-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataAppendToForm1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToForm2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormUndefined1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormUndefined2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormNull1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormNull2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormString\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataAppendToFormWrongPlatformObject\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"append.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataAppend1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppend2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppendUndefined1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppendUndefined2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppendNull1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppendNull2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataAppendEmptyBlob\",\n            \"success\": true\n          }\n        ]\n      },\n      \"constructor-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"test that FormData is correctly constructed from the form data set\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"constructor-submitter-coordinate.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"constructor-submitter.html\": {\n        \"success\": false,\n        \"cases\": []\n      },\n      \"constructor.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Constructors should throw a type error\",\n            \"success\": true\n          }\n        ]\n      },\n      \"delete-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataDeleteFromForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataDeleteFromFormNonExistentKey\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataDeleteFromFormOtherKey\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataDeleteFromEmptyForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"delete.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataDelete\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataDeleteNonExistentKey\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataDeleteOtherKey\",\n            \"success\": true\n          }\n        ]\n      },\n      \"foreach.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Iterator should return duplicate keys and non-deleted values\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Entries iterator should return duplicate keys and non-deleted values\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Keys iterator should return duplicates\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Values iterator should return non-deleted values\",\n            \"success\": true\n          }\n        ]\n      },\n      \"get-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataGetFromForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataGetFromFormNull\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataGetFromEmptyForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataGetAllFromForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataGetAllFromFormNull\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataGetAllFromEmptyForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"get.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataGet\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataGetNull1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataGetNull2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataGetAll\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataGetAllEmpty1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataGetAllEmpty2\",\n            \"success\": true\n          }\n        ]\n      },\n      \"has-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataHasFromForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataHasFromFormNull\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataHasFromEmptyForm\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"has.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataHas\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataHasEmpty1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataHasEmpty2\",\n            \"success\": true\n          }\n        ]\n      },\n      \"iteration.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"Iteration skips elements removed while iterating\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Removing elements already iterated over causes an element to be skipped during iteration\",\n            \"success\": true\n          },\n          {\n            \"name\": \"Appending a value pair during iteration causes it to be reached during iteration\",\n            \"success\": true\n          }\n        ]\n      },\n      \"set-blob.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"blob without type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"blob with type\",\n            \"success\": true\n          },\n          {\n            \"name\": \"blob with custom name\",\n            \"success\": true\n          },\n          {\n            \"name\": \"file without lastModified or custom name\",\n            \"success\": true\n          },\n          {\n            \"name\": \"file with lastModified and custom name\",\n            \"success\": true\n          }\n        ]\n      },\n      \"set-formelement.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataSetToForm1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToForm2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormUndefined1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormUndefined2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormNull1\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormNull2\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormString\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          },\n          {\n            \"name\": \"testFormDataSetToFormWrongPlatformObject\",\n            \"success\": false,\n            \"message\": \"document is not defined\"\n          }\n        ]\n      },\n      \"set.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"testFormDataSet1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSet2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSetUndefined1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSetUndefined2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSetNull1\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSetNull2\",\n            \"success\": true\n          },\n          {\n            \"name\": \"testFormDataSetEmptyBlob\",\n            \"success\": true\n          }\n        ]\n      },\n      \"submitter-coordinate-value.html\": {\n        \"success\": false,\n        \"cases\": []\n      }\n    },\n    \"formdata.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"empty formdata\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"formdata with string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"formdata with named string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"getresponseheader.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"getResponseHeader('content-length') expects 0\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"getResponseHeader('content-length') expects 0, 0\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"getResponseHeader('double-trouble') expects , \",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"getResponseHeader('foo-test') expects 1, 2, 3\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"getResponseHeader('heya') expects , \\u000b\\f, 1, , , 2\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"getResponseHeader('www-authenticate') expects 1, 2, 3, 4\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"historical.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Support for responseType = moz-blob\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Support for responseType = moz-chunked-text\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Support for responseType = moz-chunked-arraybuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"idlharness.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"idl_test setup\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: document is not defined\\\"\"\n        },\n        {\n          \"name\": \"idl_test validation\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Partial interface Document: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Partial interface Document[2]: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Document includes NonElementParentNode: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Document includes DocumentOrShadowRoot: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Document includes ParentNode: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Document includes XPathEvaluatorBase: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Document includes GlobalEventHandlers: member names are unique\",\n          \"success\": true\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: existence and properties of interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface object length\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface object name\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: existence and properties of interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onloadstart\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onprogress\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onabort\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onerror\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onload\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute ontimeout\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: attribute onloadend\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestEventTarget\\\" expected property \\\"XMLHttpRequestEventTarget\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface: existence and properties of interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface object length\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface object name\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface: existence and properties of interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequestUpload\\\" expected property \\\"XMLHttpRequestUpload\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequestUpload must be primary interface of (new XMLHttpRequest()).upload\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Stringification of (new XMLHttpRequest()).upload\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onloadstart\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onprogress\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onabort\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onerror\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onload\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"ontimeout\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: (new XMLHttpRequest()).upload must inherit property \\\"onloadend\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: existence and properties of interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface object length\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface object name\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: existence and properties of interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute onreadystatechange\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant UNSENT on interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant UNSENT on interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant OPENED on interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant OPENED on interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant HEADERS_RECEIVED on interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant HEADERS_RECEIVED on interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant LOADING on interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant LOADING on interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant DONE on interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: constant DONE on interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute readyState\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation open(ByteString, USVString)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation open(ByteString, USVString, boolean, optional USVString?, optional USVString?)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation setRequestHeader(ByteString, ByteString)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute timeout\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute withCredentials\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute upload\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation send(optional (Document or XMLHttpRequestBodyInit)?)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation abort()\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute responseURL\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute status\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute statusText\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation getResponseHeader(ByteString)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation getAllResponseHeaders()\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: operation overrideMimeType(DOMString)\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute responseType\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute response\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute responseText\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: attribute responseXML\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"XMLHttpRequest\\\" expected property \\\"XMLHttpRequest\\\" missing\"\n        },\n        {\n          \"name\": \"XMLHttpRequest must be primary interface of new XMLHttpRequest()\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"Stringification of new XMLHttpRequest()\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"onreadystatechange\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"UNSENT\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"OPENED\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"HEADERS_RECEIVED\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"LOADING\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"DONE\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"readyState\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"open(ByteString, USVString)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling open(ByteString, USVString) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"open(ByteString, USVString, boolean, optional USVString?, optional USVString?)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling open(ByteString, USVString, boolean, optional USVString?, optional USVString?) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"setRequestHeader(ByteString, ByteString)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling setRequestHeader(ByteString, ByteString) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"timeout\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"withCredentials\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"upload\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"send(optional (Document or XMLHttpRequestBodyInit)?)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling send(optional (Document or XMLHttpRequestBodyInit)?) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"abort()\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"responseURL\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"status\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"statusText\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"getResponseHeader(ByteString)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling getResponseHeader(ByteString) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"getAllResponseHeaders()\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"overrideMimeType(DOMString)\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: calling overrideMimeType(DOMString) on new XMLHttpRequest() with too few arguments must throw TypeError\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"responseType\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"response\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"responseText\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequest interface: new XMLHttpRequest() must inherit property \\\"responseXML\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onloadstart\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onprogress\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onabort\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onerror\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onload\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"ontimeout\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"XMLHttpRequestEventTarget interface: new XMLHttpRequest() must inherit property \\\"onloadend\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"FormData interface: existence and properties of interface object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface object length\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface object name\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: existence and properties of interface prototype object\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation append(USVString, USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation append(USVString, Blob, optional USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation delete(USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation get(USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation getAll(USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation has(USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation set(USVString, USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: operation set(USVString, Blob, optional USVString)\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: iterable<USVString, FormDataEntryValue>\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData must be primary interface of new FormData()\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Stringification of new FormData()\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"append(USVString, USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling append(USVString, USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"append(USVString, Blob, optional USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling append(USVString, Blob, optional USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"delete(USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling delete(USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"get(USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling get(USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"getAll(USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling getAll(USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"has(USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling has(USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"set(USVString, USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling set(USVString, USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: new FormData() must inherit property \\\"set(USVString, Blob, optional USVString)\\\" with the proper type\",\n          \"success\": true\n        },\n        {\n          \"name\": \"FormData interface: calling set(USVString, Blob, optional USVString) on new FormData() with too few arguments must throw TypeError\",\n          \"success\": true\n        },\n        {\n          \"name\": \"ProgressEvent interface: existence and properties of interface object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface object length\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface object name\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: existence and properties of interface prototype object\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: existence and properties of interface prototype object's \\\"constructor\\\" property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: existence and properties of interface prototype object's @@unscopables property\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: attribute lengthComputable\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: attribute loaded\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: attribute total\",\n          \"success\": false,\n          \"message\": \"assert_own_property: self does not have own property \\\"ProgressEvent\\\" expected property \\\"ProgressEvent\\\" missing\"\n        },\n        {\n          \"name\": \"ProgressEvent must be primary interface of new ProgressEvent(\\\"type\\\")\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: ProgressEvent is not defined\\\"\"\n        },\n        {\n          \"name\": \"Stringification of new ProgressEvent(\\\"type\\\")\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: ProgressEvent is not defined\\\"\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: new ProgressEvent(\\\"type\\\") must inherit property \\\"lengthComputable\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: ProgressEvent is not defined\\\"\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: new ProgressEvent(\\\"type\\\") must inherit property \\\"loaded\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: ProgressEvent is not defined\\\"\"\n        },\n        {\n          \"name\": \"ProgressEvent interface: new ProgressEvent(\\\"type\\\") must inherit property \\\"total\\\" with the proper type\",\n          \"success\": false,\n          \"message\": \"assert_equals: Unexpected exception when evaluating object expected null but got object \\\"ReferenceError: ProgressEvent is not defined\\\"\"\n        }\n      ]\n    },\n    \"json.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Ensure the correct JSON parser is used\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Ensure UTF-16 results in an error\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"loadstart-and-state.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"open() during loadstart\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"abort() during loadstart\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"open-after-stop.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"over-1-meg.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"over-1-meg\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"overridemimetype-blob.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Use text/xml as fallback MIME type\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Use text/xml as fallback MIME type, 2\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Loading data…\",\n          \"success\": true\n        },\n        {\n          \"name\": \"1) MIME types need to be parsed and serialized: text/html;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"2) MIME types need to be parsed and serialized: TEXT/HTML;CHARSET=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"3) MIME types need to be parsed and serialized: text/html;charset=gbk(\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"4) MIME types need to be parsed and serialized: text/html;x=(;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"5) MIME types need to be parsed and serialized: text/html;charset=gbk;charset=windows-1255\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"6) MIME types need to be parsed and serialized: text/html;charset=();charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"7) MIME types need to be parsed and serialized: text/html;charset =gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"8) MIME types need to be parsed and serialized: text/html ;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"9) MIME types need to be parsed and serialized: text/html; charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"10) MIME types need to be parsed and serialized: text/html;charset= gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"11) MIME types need to be parsed and serialized: text/html;charset= \\\"gbk\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"12) MIME types need to be parsed and serialized: text/html;charset=\\u000bgbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"13) MIME types need to be parsed and serialized: text/html;charset=\\fgbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"14) MIME types need to be parsed and serialized: text/html;\\u000bcharset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"15) MIME types need to be parsed and serialized: text/html;\\fcharset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"16) MIME types need to be parsed and serialized: text/html;charset='gbk'\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"17) MIME types need to be parsed and serialized: text/html;charset='gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"18) MIME types need to be parsed and serialized: text/html;charset=gbk'\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"19) MIME types need to be parsed and serialized: text/html;charset=';charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"20) MIME types need to be parsed and serialized: text/html;test;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"21) MIME types need to be parsed and serialized: text/html;test=;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"22) MIME types need to be parsed and serialized: text/html;';charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"23) MIME types need to be parsed and serialized: text/html;\\\";charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"24) MIME types need to be parsed and serialized: text/html ; ; charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"25) MIME types need to be parsed and serialized: text/html;;;;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"26) MIME types need to be parsed and serialized: text/html;charset= \\\";charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"27) MIME types need to be parsed and serialized: text/html;charset=\\\";charset=foo\\\";charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"28) MIME types need to be parsed and serialized: text/html;charset=\\\"gbk\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"29) MIME types need to be parsed and serialized: text/html;charset=\\\"gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"30) MIME types need to be parsed and serialized: text/html;charset=gbk\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"31) MIME types need to be parsed and serialized: text/html;charset=\\\" gbk\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"32) MIME types need to be parsed and serialized: text/html;charset=\\\"gbk \\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"33) MIME types need to be parsed and serialized: text/html;charset=\\\"\\\\ gbk\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"34) MIME types need to be parsed and serialized: text/html;charset=\\\"\\\\g\\\\b\\\\k\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"35) MIME types need to be parsed and serialized: text/html;charset=\\\"gbk\\\"x\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"36) MIME types need to be parsed and serialized: text/html;charset=\\\"\\\";charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"37) MIME types need to be parsed and serialized: text/html;charset=\\\";charset=GBK\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"38) MIME types need to be parsed and serialized: text/html;charset={gbk}\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"39) MIME types need to be parsed and serialized: text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"40) MIME types need to be parsed and serialized: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"41) MIME types need to be parsed and serialized: text/html;a]=bar;b[=bar;c=bar\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"42) MIME types need to be parsed and serialized: text/html;valid=\\\";\\\";foo=bar\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"43) MIME types need to be parsed and serialized: text/html;in]valid=\\\";asd=foo\\\";foo=bar\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"44) MIME types need to be parsed and serialized: !#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"45) MIME types need to be parsed and serialized: x/x;x=\\\"\\t !\\\\\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"46) MIME types need to be parsed and serialized: x/x;test\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"47) MIME types need to be parsed and serialized: x/x;test=\\\"\\\\\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"48) MIME types need to be parsed and serialized: x/x;x= \",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"49) MIME types need to be parsed and serialized: x/x;x=\\t\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"50) MIME types need to be parsed and serialized: x/x\\n\\r\\t ;x=x\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"51) MIME types need to be parsed and serialized: \\n\\r\\t x/x;x=x\\n\\r\\t \",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"52) MIME types need to be parsed and serialized: x/x;\\n\\r\\t x=x\\n\\r\\t ;x=y\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"53) MIME types need to be parsed and serialized: text/html;test=ÿ;charset=gbk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"54) MIME types need to be parsed and serialized: x/x;test=�;x=x\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"55) MIME types need to be parsed and serialized: \\u000bx/x\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"56) MIME types need to be parsed and serialized: \\fx/x\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"57) MIME types need to be parsed and serialized: x/x\\u000b\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"58) MIME types need to be parsed and serialized: x/x\\f\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"59) MIME types need to be parsed and serialized: \",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"60) MIME types need to be parsed and serialized: \\t\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"61) MIME types need to be parsed and serialized: /\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"62) MIME types need to be parsed and serialized: bogus\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"63) MIME types need to be parsed and serialized: bogus/\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"64) MIME types need to be parsed and serialized: bogus/ \",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"65) MIME types need to be parsed and serialized: bogus/bogus/;\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"66) MIME types need to be parsed and serialized: </>\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"67) MIME types need to be parsed and serialized: (/)\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"68) MIME types need to be parsed and serialized: ÿ/ÿ\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"69) MIME types need to be parsed and serialized: text/html(;doesnot=matter\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"70) MIME types need to be parsed and serialized: {/}\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"71) MIME types need to be parsed and serialized: Ā/Ā\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"72) MIME types need to be parsed and serialized: text /html\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"73) MIME types need to be parsed and serialized: text/ html\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"74) MIME types need to be parsed and serialized: \\\"text/html\\\"\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"overridemimetype-done-state.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"overridemimetype-edge-cases.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"overrideMimeType() is not reset by open(), basic\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"overrideMimeType() is not reset by open()\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"If charset is not overridden by overrideMimeType() the original continues to be used\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Charset can be overridden by overrideMimeType() with a bogus charset\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"overridemimetype-unsent-state-force-shiftjis.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: overrideMimeType() in unsent state, enforcing Shift-JIS encoding\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"progressevent-constructor.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Default event values.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"There must not be a initProgressEvent().\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Basic test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"ECMAScript value conversion test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Positive integer number test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Zero test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Decimal number test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Mixed integer and decimal number test.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Negative number.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"ProgressEventInit members must be matched case-sensitively.\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        }\n      ]\n    },\n    \"progressevent-interface.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"progressevent-interface\",\n          \"success\": false,\n          \"message\": \"assert_equals: expected \\\"function\\\" but got \\\"undefined\\\"\"\n        },\n        {\n          \"name\": \"interface prototype object\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"progressevent-interface 1\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"progressevent-interface 2\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"progressevent-interface 3\",\n          \"success\": false,\n          \"message\": \"ProgressEvent is not defined\"\n        },\n        {\n          \"name\": \"Interface objects properties should not be Enumerable\",\n          \"success\": true\n        },\n        {\n          \"name\": \"Should be able to delete ProgressEvent.\",\n          \"success\": false,\n          \"message\": \"assert_true: Interface should exist. expected true got false\"\n        }\n      ]\n    },\n    \"request-content-length.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Uploads need to set the Content-Length header\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Fetched blob: URLs set the Content-Length header\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"response-body-errors.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Synchronous XMLHttpRequest should throw on bad chunk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Asynchronous XMLHttpRequest should clear response on bad chunk\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"responseText-status.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"responseType-document-in-worker.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"responseXML-unavailable-in-worker.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"responsetype.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Initial value of responseType\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"json\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"document\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"arraybuffer\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"blob\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"text\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is UNSENT.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is OPENED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is HEADERS_RECEIVED.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is LOADING.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is DONE.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is OPENED and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Set responseType to \\\"nosuchtype\\\" when readyState is DONE and the sync flag is set.\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"responseurl.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"404 response has proper responseURL\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Redirected response has proper responseURL\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"responsexml-invalid-type.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"responsexml-invalid-type\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        }\n      ]\n    },\n    \"security-consideration.sub.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"security-consideration\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-blob-with-no-mime-type.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Synchronous blob loading with no mime type [POST]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Asynchronous blob loading with no mime type [POST]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Synchronous blob loading with no mime type [PUT]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Asynchronous blob loading with no mime type [PUT]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Synchronous blob loading with invalid mime type [POST]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Asynchronous blob loading with invalid mime type [POST]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Synchronous blob loading with invalid mime type [PUT]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"Asynchronous blob loading with invalid mime type [PUT]\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-arraybuffer.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"send-data-arraybuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-arraybufferview.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"send-data-arraybufferview\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-es-object.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sending a plain empty object\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending the ES Math object\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-formdata.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"send-data-formdata\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-sharedarraybuffer.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sending a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Int8Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Uint8Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Uint8ClampedArray backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Int16Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Uint16Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Int32Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Uint32Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a BigInt64Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a BigUint64Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Float16Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Float32Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a Float64Array backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"sending a DataView backed by a SharedArrayBuffer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-data-string-invalid-unicode.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"invalid unicode '\\\\u{d83d}' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode '\\\\u{d83d}ab' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode 'a\\\\u{d83d}b' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode 'ab\\\\u{d83d}' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode '\\\\u{dc94}' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode '\\\\u{dc94}ab' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode 'a\\\\u{dc94}b' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"invalid unicode 'ab\\\\u{dc94}' should be fixed with replacement character\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"valid unicode should be sent correctly\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-send.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"XMLHttpRequest: send() - send()\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"send-usp.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"setrequestheader-combining.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"setRequestHeader() combining header values\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"status.h2.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"statusText over H2 for status 200 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 210 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 400 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 404 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 410 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 500 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"statusText over H2 for status 502 should be the empty string\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"sync-no-progress.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"progress event should not be fired by sync XHR\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"sync-no-timeout.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Sync XHR should not have a timeout\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"sync-xhr-and-window-onload.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"sync XHR should not fire window.onload synchronously\",\n          \"success\": false,\n          \"message\": \"window.addEventListener is not a function\"\n        }\n      ]\n    },\n    \"sync-xhr-supported-by-feature-policy.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"document.featurePolicy.features should advertise sync-xhr.\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"template-element.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"template-element\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"template-element 1\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"template-element 2\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"thrown-error-in-events.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"errors thrown in XMLHttpRequest's load event (using addEventListener) goes to window.onerror\",\n          \"success\": false,\n          \"message\": \"window.addEventListener is not a function\"\n        },\n        {\n          \"name\": \"errors thrown in XMLHttpRequest's load event (using onload) goes to window.onerror\",\n          \"success\": false,\n          \"message\": \"window.addEventListener is not a function\"\n        }\n      ]\n    },\n    \"timeout-multiple-fetches.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Redirects should not reset the timer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        },\n        {\n          \"name\": \"CORS preflights should not reset the timer\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"xhr-authorization-redirect.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"getAuthorizationHeaderValue - no redirection\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"getAuthorizationHeaderValue - same origin redirection\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        },\n        {\n          \"name\": \"getAuthorizationHeaderValue - cross origin redirection\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"ReferenceError: XMLHttpRequest is not defined\\\"\"\n        }\n      ]\n    },\n    \"xhr-timeout-longtask.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Long tasks should not trigger load timeout\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"xmlhttprequest-sync-block-defer-scripts.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-sync-block-scripts.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"xmlhttprequest-sync-block-scripts\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"xmlhttprequest-sync-default-feature-policy.sub.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"Default \\\"sync-xhr\\\" feature policy [\\\"*\\\"] allows the top-level document.\",\n          \"success\": false,\n          \"message\": \"promise_test: Unhandled rejection with value: object \\\"[object Object]\\\"\"\n        },\n        {\n          \"name\": \"Default \\\"sync-xhr\\\" feature policy [\\\"*\\\"] allows same-origin iframes.\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        },\n        {\n          \"name\": \"Default \\\"sync-xhr\\\" feature policy [\\\"*\\\"] allows cross-origin iframes.\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        },\n        {\n          \"name\": \"Feature policy \\\"sync-xhr\\\" can be disabled in cross-origin iframes using \\\"allow\\\" attribute.\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        },\n        {\n          \"name\": \"Feature policy \\\"sync-xhr\\\" can be disabled in same-origin iframes using \\\"allow\\\" attribute.\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"xmlhttprequest-sync-not-hang-scriptloader.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-aborted.html?aborted%20immediately%20after%20send()\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-aborted.html?call%20abort()%20after%20TIME_NORMAL_LOAD\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-aborted.html?only%20open()ed,%20not%20aborted\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-abortedonmain.html?abort()%20from%20a%200ms%20timeout\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-abortedonmain.html?aborted%20after%20TIME_DELAY\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overrides.html?timeout%20disabled%20after%20initially%20set\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overrides.html?timeout%20enabled%20after%20initially%20disabled\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overrides.html?timeout%20overrides%20load%20after%20a%20delay\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overridesexpires.html?timeout%20set%20to%20expired%20value%20before%20load%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overridesexpires.html?timeout%20set%20to%20expiring%20value%20after%20load%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-overridesexpires.html?timeout%20set%20to%20non-expiring%20value%20after%20timeout%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-reused.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"xmlhttprequest-timeout-reused\",\n          \"success\": false,\n          \"message\": \"XMLHttpRequest is not defined\"\n        }\n      ]\n    },\n    \"xmlhttprequest-timeout-simple.html?load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-simple.html?no%20time%20out%20scheduled,%20load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-simple.html?timeout%20hit%20before%20load\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-synconmain.html?timeout%20after%20open\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-synconmain.html?timeout%20before%20open\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-twice.html?load%20fires%20normally%20with%20no%20timeout%20set,%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-twice.html?load%20fires%20normally%20with%20same%20timeout%20set%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-twice.html?timeout%20fires%20normally%20with%20same%20timeout%20set%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-aborted.html?aborted%20immediately%20after%20send()\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-aborted.html?call%20abort()%20after%20TIME_NORMAL_LOAD\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-aborted.html?only%20open()ed,%20not%20aborted\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overrides.html?timeout%20disabled%20after%20initially%20set\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overrides.html?timeout%20enabled%20after%20initially%20disabled\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overrides.html?timeout%20overrides%20load%20after%20a%20delay\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overridesexpires.html?timeout%20set%20to%20expired%20value%20before%20load%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overridesexpires.html?timeout%20set%20to%20expiring%20value%20after%20load%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-overridesexpires.html?timeout%20set%20to%20non-expiring%20value%20after%20timeout%20fires\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-simple.html?load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-simple.html?no%20time%20out%20scheduled,%20load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-simple.html?timeout%20hit%20before%20load\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-synconworker.html?load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-synconworker.html?no%20time%20out%20scheduled,%20load%20fires%20normally\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-synconworker.html?timeout%20hit%20before%20load\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-twice.html?load%20fires%20normally%20with%20no%20timeout%20set,%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-twice.html?load%20fires%20normally%20with%20same%20timeout%20set%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"xmlhttprequest-timeout-worker-twice.html?timeout%20fires%20normally%20with%20same%20timeout%20set%20twice\": {\n      \"success\": false,\n      \"cases\": []\n    }\n  },\n  \"eventsource\": {\n    \"dedicated-worker\": {\n      \"eventsource-constructor-no-new.any.html\": {\n        \"success\": true,\n        \"cases\": [\n          {\n            \"name\": \"eventsource-constructor-no-new\",\n            \"success\": true\n          }\n        ]\n      }\n    },\n    \"event-data.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"event-data\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-close.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-close\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-constructor-document-domain.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-constructor-document-domain\",\n          \"success\": false,\n          \"message\": \"document is not defined\"\n        }\n      ]\n    },\n    \"eventsource-constructor-empty-url.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"EventSource constructor with an empty url.\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-constructor-non-same-origin.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"eventsource-constructor-stringify.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"eventsource-constructor-url-bogus.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-constructor-url-bogus\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-cross-origin.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"eventsource-eventtarget.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-eventtarget\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-onmessage-trusted.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"EventSource message events are trusted\",\n          \"success\": false,\n          \"message\": \"assert_equals: expected true but got false\"\n        }\n      ]\n    },\n    \"eventsource-onmessage.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-onmessage\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-onopen.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-onopen\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-prototype.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-prototype\",\n          \"success\": true\n        }\n      ]\n    },\n    \"eventsource-reconnect.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"eventsource-request-cancellation.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"eventsource-url.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"eventsource-url\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-bom-2.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-bom-2\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-bom.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-bom\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-comments.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-comments\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-data-before-final-empty-line.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-data.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-data\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-event-empty.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-event-empty\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-event.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-event\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-id-2.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-id-3.window.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"EventSource: lastEventId persists\",\n          \"success\": true\n        },\n        {\n          \"name\": \"EventSource: lastEventId resets\",\n          \"success\": true\n        },\n        {\n          \"name\": \"EventSource: lastEventId resets (id without colon)\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-id-null.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-id.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-parsing.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-parsing\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-retry-bogus.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-retry-empty.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-retry-empty\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-field-retry.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"format-field-unknown.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-field-unknown\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-leading-space.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-leading-space\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-mime-bogus.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-mime-bogus\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-mime-trailing-semicolon.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-mime-trailing-semicolon\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-mime-valid-bogus.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-mime-valid-bogus\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-newlines.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-newlines\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-null-character.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-null-character\",\n          \"success\": true\n        }\n      ]\n    },\n    \"format-utf-8.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"format-utf-8\",\n          \"success\": true\n        }\n      ]\n    },\n    \"request-accept.any.html\": {\n      \"success\": true,\n      \"cases\": [\n        {\n          \"name\": \"request-accept\",\n          \"success\": true\n        }\n      ]\n    },\n    \"request-cache-control.any.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"request-credentials.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"request-redirect.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    },\n    \"request-status-error.window.html\": {\n      \"success\": false,\n      \"cases\": []\n    }\n  }\n}\n"
  },
  {
    "path": "test/web-platform-tests/runner/certs/cacert.key",
    "content": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIcCc77/XI8vACAggA\nMAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECD9GzJi6B3uZBIIEyFt39/iCNVxN\nyh8hhgdG5dHjoPq6Hyj7Zb6aSCmh+uGVkxdJOKlVGriI9V0tWjO0QitRPiRjUg/L\nPAO/p2ojBGzt2PPH0LHhYkAU3qzE/Uwx6mrL8dOsiftXLAynqh9okNrsuu9cKmSo\nto7NuqkXFVb8rCn1TOFCES9YIoDt7lm7wgksWiTCxa2DNbx4C9xHuyhgxCl+cLqH\nFnEt1dAMYh6ShSzPsuHcUVqR8FJf03amSqlgjuYx2uRJ6msaqaYRgqEUtdLALMfa\nT0ipH86+/D1enmRmoo+tzABigeA8iiRQfmehbpb6yWgQ68GAT4gF1JgcTk2aELBx\nQUI2RUZEH7BZfqwSEZouHEgryW4NhVCndf1bUkdjJzVPIiV3YYCfyKQn4qHyuMJc\nPc8EdaRM7dEDSbtq1S4arZAAdR9XwH8CgSOy2bTt8NWc8HM4g6MtERsX/ICJh6po\nRXP6i6dG0i1UQOsHXwyB5yBf5BnlH/uWvLuVSEEThfd4gycJ1co6OQePlkHhDend\nLHsm8YUSJ8IcL3YO/qXEj71yCp//BwQsSjmLvOZw8WWsB2WSbAZq+oI2fb4dRtPF\nB/Y6aD/dqV21JnxlK8y9WvvoUVTHt6zN5083IB3Aun3x840y7RkKtS03f0J3Sv7E\nJyduGwTvnYk6LbI4KAJsx44SYRKBBVa/aBlfFWfmSPgw0vySXMEc0mHIiqQRQ7Ld\nm6pgt2KM80wenGbxpQ6PdmYMNDKDGkVBiAdjEDnPqYZitKaiu211FDH6+RLasG7s\npFNz7SJb4Zcwt2i8p54YZ7SYv/HoQmIdrNY21Rop+RPkHXiWHkNLDrl7u3WdgErf\niwLpS/WGTbTKE3wxM2JTTyk6/qBH3Crvx8jabuyFnCmVbEnZDfZDWzAGQ6B3YixK\n7IRabwSOBrSHQWIcOPAZR8E8p+qH8g+jabUnI9DfHVvqQWGHbHw/mcMO5YfcqkYz\n64wTumCAkcQqR/E+B4YHSiuCFQJs0g9mB/jhN85A7U4ZyfmZjx/3TpxuRkBUXRW5\nNue+/68wovZcRS7uFm5nwoBN89HwyNgAhxCGcie3pnzmj9AsPHpF4vUbZ1oUth1L\nXoKMb+qgHv5I0WYqJA7n+zya609gkmCnVB3ZYT7n8fYu1hUHFqXjhFQiq9h+ADxT\nxARagVn7qX95Y9dYC4aiqHyw+bUl8AlBc+7Oefj+rtQjsNKq08HxAhg3YLQB2Py6\nN69N0e0Dcymo5YVkpIZ/CxlpJgzgCVauaVogeLL4AO+1zedsJiBAd/s8ctenwDQX\nSfScZ7Jnp9q8Kh0ehIihNHnfcTKLrvQQ/MLXxOS2kU4yCHCw6DjJmO3Lr8bPrqJi\nrTbnWvbAdELHFu4JZtSfiKMPvXVNXz1t1lz4/ZyzjKmwHj5yBwLMaLNM47SCwTWo\n1Un1zg0lt13+2KqNK+Fmu//Y9ZlitWswOMCDDWvkPpwS5URq9yrUmIoGoM8O9ZgB\nVogi3Mtxo5cI3raZR+NZ9Ybw5MSHsCYeKaEFfEVytFTvnXzpSvyErjJqjzvpL4x+\nTLT6uHqBBv8u9DLp9GBpy48Ub4qoJOh+WddzVHxrWu53OJ4MNM+ytIA9j6lODjBM\nC69fZVEQ7Numypy150P9ig==\n-----END ENCRYPTED PRIVATE KEY-----\n"
  },
  {
    "path": "test/web-platform-tests/runner/certs/cacert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIW5zCCFc+gAwIBAgIDCavTMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl\nYi1wbGF0Zm9ybS10ZXN0czAeFw0yNTA4MjkyMDMyMzBaFw0yNjA4MjkyMDMyMzBa\nMB0xGzAZBgNVBAMMEndlYi1wbGF0Zm9ybS10ZXN0czCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAN/4LYUrh84ht9Ha39KtH8JxnOo0cvZ5I2+3rj7ntl5a\nsmc0Ul4/NtCWwZif7Nm5PGeQ7PFsKR6A0lG+HMLhp0YLpET9UO5IgytS/YdM2B45\nH76J8i1rytzcudD8D6/d2orCUa9Y2+ovyBB1ZD9vGVJqMDMZxlBNZ7qNhx3bwKHf\nek8UbO8PF7LKTnq/1AwODNrMC5h6XBzvy0ecu7rqkQYpbozOOEsV9lalumkU1WBW\nLLGKgXcso7n52kqxHhOzZQrXbFEcx/HRCfX52j6cAu7V+0yEi9yyQ2Zo7PnKlJjN\nEaeD6TNUSq/16pSW7zDSZSD3qkDcV6R9Y4OIBqiNtsUCAwEAAaOCFC4wghQqMAwG\nA1UdEwQFMAMBAf8wHQYDVR0OBBYEFFiBk80EIpPnvCX2PP2YW3J0msnDMEcGA1Ud\nIwRAMD6AFFiBk80EIpPnvCX2PP2YW3J0msnDoSGkHzAdMRswGQYDVQQDDBJ3ZWIt\ncGxhdGZvcm0tdGVzdHOCAwmr0zALBgNVHQ8EBAMCAgQwggoFBgNVHR4Eggn8MIIJ\n+KCCCfQwE4IRd2ViLXBsYXRmb3JtLnRlc3QwF4IVbm90LXdlYi1wbGF0Zm9ybS50\nZXN0MBeCFXd3dy53ZWItcGxhdGZvcm0udGVzdDAYghZ3d3cxLndlYi1wbGF0Zm9y\nbS50ZXN0MBiCFnd3dzIud2ViLXBsYXRmb3JtLnRlc3QwG4IZd3d3Lm5vdC13ZWIt\ncGxhdGZvcm0udGVzdDAbghl3d3cud3d3LndlYi1wbGF0Zm9ybS50ZXN0MByCGnd3\ndzIud3d3LndlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzEud3d3LndlYi1wbGF0Zm9y\nbS50ZXN0MByCGnd3dzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dy53d3cy\nLndlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dy53d3cxLndlYi1wbGF0Zm9ybS50ZXN0\nMByCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzIud3d3MS53ZWIt\ncGxhdGZvcm0udGVzdDAdght3d3cxLnd3dzEud2ViLXBsYXRmb3JtLnRlc3QwHYIb\nd3d3Mi53d3cyLndlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzEud3d3Mi53ZWItcGxh\ndGZvcm0udGVzdDAfgh13d3cud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdDAggh53\nd3cud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwIIIed3d3Lnd3dzIubm90LXdl\nYi1wbGF0Zm9ybS50ZXN0MCCCHnd3dzEud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz\ndDAggh54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3QwIIIed3d3Mi53d3cu\nbm90LXdlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRm\nb3JtLnRlc3QwIYIfd3d3MS53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdDAhgh93\nd3cxLnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzIud3d3Mi5ub3Qt\nd2ViLXBsYXRmb3JtLnRlc3QwJIIieG4tLWx2ZS02bGFkLm5vdC13ZWItcGxhdGZv\ncm0udGVzdDAkgiJ3d3cueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0MCSC\nInhuLS1sdmUtNmxhZC53d3cud2ViLXBsYXRmb3JtLnRlc3QwJYIjd3d3MS54bi0t\nbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3QwJYIjd3d3Mi54bi0tbHZlLTZsYWQu\nd2ViLXBsYXRmb3JtLnRlc3QwJYIjeG4tLWx2ZS02bGFkLnd3dzIud2ViLXBsYXRm\nb3JtLnRlc3QwJYIjeG4tLWx2ZS02bGFkLnd3dzEud2ViLXBsYXRmb3JtLnRlc3Qw\nKIImeG4tLWx2ZS02bGFkLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKIImd3d3\nLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYIneG4tLWx2ZS02\nbGFkLnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCmCJ3d3dzIueG4tLWx2ZS02\nbGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdDApgid3d3cxLnhuLS1sdmUtNmxhZC5u\nb3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYIneG4tLWx2ZS02bGFkLnd3dzIubm90LXdl\nYi1wbGF0Zm9ybS50ZXN0MCuCKXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLndlYi1w\nbGF0Zm9ybS50ZXN0MC2CK3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQud2ViLXBs\nYXRmb3JtLnRlc3QwL4IteG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3LndlYi1w\nbGF0Zm9ybS50ZXN0MC+CLXd3dy54bi0tbjhqNmRzNTNsd3drcnFodjI4YS53ZWIt\ncGxhdGZvcm0udGVzdDAvgi14bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2Vi\nLXBsYXRmb3JtLnRlc3QwMIIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3Mi53\nZWItcGxhdGZvcm0udGVzdDAwgi53d3cyLnhuLS1uOGo2ZHM1M2x3d2tycWh2Mjhh\nLndlYi1wbGF0Zm9ybS50ZXN0MDCCLnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYy\nOGEud2ViLXBsYXRmb3JtLnRlc3QwMIIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEu\nd3d3MS53ZWItcGxhdGZvcm0udGVzdDAxgi94bi0tbHZlLTZsYWQueG4tLWx2ZS02\nbGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdDAzgjF3d3cueG4tLW44ajZkczUzbHd3\na3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MDOCMXhuLS1uOGo2ZHM1M2x3\nd2tycWh2MjhhLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwNIIyeG4tLW44ajZk\nczUzbHd3a3JxaHYyOGEud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwNIIyeG4t\nLW44ajZkczUzbHd3a3JxaHYyOGEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3Qw\nNIIyd3d3MS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3Jt\nLnRlc3QwNIIyd3d3Mi54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBs\nYXRmb3JtLnRlc3QwOII2eG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4tLWx2ZS02\nbGFkLndlYi1wbGF0Zm9ybS50ZXN0MDiCNnhuLS1sdmUtNmxhZC54bi0tbjhqNmRz\nNTNsd3drcnFodjI4YS53ZWItcGxhdGZvcm0udGVzdDA8gjp4bi0tbHZlLTZsYWQu\neG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MDyC\nOnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBs\nYXRmb3JtLnRlc3QwQ4JBeG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4tLW44ajZk\nczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3QwR4JFeG4tLW44ajZkczUz\nbHd3a3JxaHYyOGEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0\nZm9ybS50ZXN0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMIIJhQYDVR0RBIIJfDCCCXiC\nEXdlYi1wbGF0Zm9ybS50ZXN0ghVub3Qtd2ViLXBsYXRmb3JtLnRlc3SCFXd3dy53\nZWItcGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxhdGZvcm0udGVzdIIWd3d3Mi53\nZWItcGxhdGZvcm0udGVzdIIZd3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3\nLnd3dy53ZWItcGxhdGZvcm0udGVzdIIad3d3Mi53d3cud2ViLXBsYXRmb3JtLnRl\nc3SCGnd3dzEud3d3LndlYi1wbGF0Zm9ybS50ZXN0ghp3d3cyLm5vdC13ZWItcGxh\ndGZvcm0udGVzdIIad3d3Lnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCGnd3dy53d3cx\nLndlYi1wbGF0Zm9ybS50ZXN0ghp3d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIb\nd3d3Mi53d3cxLndlYi1wbGF0Zm9ybS50ZXN0ght3d3cxLnd3dzEud2ViLXBsYXRm\nb3JtLnRlc3SCG3d3dzIud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIbd3d3MS53d3cy\nLndlYi1wbGF0Zm9ybS50ZXN0gh13d3cud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz\ndIIed3d3Lnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gh53d3cud3d3Mi5ub3Qt\nd2ViLXBsYXRmb3JtLnRlc3SCHnd3dzEud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz\ndIIeeG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0gh53d3cyLnd3dy5ub3Qt\nd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRl\nc3SCH3d3dzEud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3MS5u\nb3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3Mi5ub3Qtd2ViLXBsYXRmb3Jt\nLnRlc3SCInhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCInd3dy54\nbi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC53d3cu\nd2ViLXBsYXRmb3JtLnRlc3SCI3d3dzEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9y\nbS50ZXN0giN3d3cyLnhuLS1sdmUtNmxhZC53ZWItcGxhdGZvcm0udGVzdIIjeG4t\nLWx2ZS02bGFkLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCI3huLS1sdmUtNmxhZC53\nd3cxLndlYi1wbGF0Zm9ybS50ZXN0giZ4bi0tbHZlLTZsYWQud3d3Lm5vdC13ZWIt\ncGxhdGZvcm0udGVzdIImd3d3LnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3Jt\nLnRlc3SCJ3huLS1sdmUtNmxhZC53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIn\nd3d3Mi54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0gid3d3cxLnhu\nLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53\nd3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIpeG4tLW44ajZkczUzbHd3a3JxaHYy\nOGEud2ViLXBsYXRmb3JtLnRlc3SCK3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQu\nd2ViLXBsYXRmb3JtLnRlc3SCLXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dy53\nZWItcGxhdGZvcm0udGVzdIItd3d3LnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLndl\nYi1wbGF0Zm9ybS50ZXN0gi14bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2Vi\nLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzIud2Vi\nLXBsYXRmb3JtLnRlc3SCLnd3dzIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi\nLXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi\nLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzEud2Vi\nLXBsYXRmb3JtLnRlc3SCL3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQubm90LXdl\nYi1wbGF0Zm9ybS50ZXN0gjF3d3cueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90\nLXdlYi1wbGF0Zm9ybS50ZXN0gjF4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cu\nbm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53\nd3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIyeG4tLW44ajZkczUzbHd3a3JxaHYy\nOGEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCMnd3dzEueG4tLW44ajZkczUz\nbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ3d3cyLnhuLS1uOGo2\nZHM1M2x3d2tycWh2MjhhLm5vdC13ZWItcGxhdGZvcm0udGVzdII2eG4tLW44ajZk\nczUzbHd3a3JxaHYyOGEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0gjZ4\nbi0tbHZlLTZsYWQueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3Jt\nLnRlc3SCOnhuLS1sdmUtNmxhZC54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qt\nd2ViLXBsYXRmb3JtLnRlc3SCOnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1s\ndmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCQXhuLS1uOGo2ZHM1M2x3d2ty\ncWh2MjhhLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLndlYi1wbGF0Zm9ybS50ZXN0\ngkV4bi0tbjhqNmRzNTNsd3drcnFodjI4YS54bi0tbjhqNmRzNTNsd3drcnFodjI4\nYS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwDQYJKoZIhvcNAQELBQADggEBACMHIA8A\nYh01/jQdnwdl6LlJu0Ulhu2pt1kmI5HvXnvwxb/mh5vABOmU/LFiqtqdmuj+hd44\nyqsSXeY1tAO9aU2wUZJjYIqtIuITINtCCwi11ZBAs5dk9gAD/pj1VAgHilZJOGE9\ndBr8sNfPm+6vBEcM8HkgNtsqj7Ju5SWEqKX8fAHQA6zFvG2Q8Azv3QRbZyeXlAyC\nr7w3Dsg2KCRzTh/mV3Cdsgvt3qAawxIKed+4bLFMOB8zxMsrEs5M/xE5coOIrWct\nY9d+E1iC1HQNjAAvbglEnJ3VyyoBVHSKf8QFdoS+hojCTJIZfHw2YcdYioEwFqSN\n/PYetxweEfh0ne0=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/web-platform-tests/runner/certs/web-platform.test.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5f4JjnGrllttq\nJhhhstKOIj5CCltDbitTZxmryLhB82KV9cvKshXYRVPEId7PMoFEXHcQb0k+/m+K\ndNtRYB6HYJ72wIcyfbGLBu3yxux5mOjRLhMNOAY/IURfWFzFA6DjCbie4mo/099J\nkkOT/JmFyRYWjTukINw4NQ9fXjDn+OyY9gEA2Z3rVj4RqaggKZUvOINGTkMhvHb+\nTgczCw0rpfjL1rcQPve6h/2VfP+VhhAROcVmENQta1BnK511RXnTAVyjRlo8WL9Q\nMOwE8imB28+fWa8sUygwEwV120yr4Ktq+AL6bSA7VCAifT6wOswaZBlo3ymZeX03\nmvM+7/zvAgMBAAECggEAAtJqxGMrySRbYCkGM+5Jf7oLomuP6d2yMQmffVqHWxF9\nzXQ4znOZG3oqhRBS2GoXR1f0w749qgSq47tVXs6MiFiy8mQJan45Bjlrw0a9cDgg\nSpZd2dShW7KA3MZ2WtTsTKfoNBqTrTbm7bKY7pZoK33cpSku4BdT4lMEUdoX+l9r\nbwbxspJiRWmMEJvTIuk0I+oyH9ISJ2gL9ik18lf2brzqU7+sH+Gfpym+/FwmCmYt\nu/Zkr8aoQjda8vvTo6b7kVFwyD9UnynQxfhSbXn/K588ns90locxU3Wsng5rihKM\noTK+UQm6i+qiR0C3BSb3urE4cIl3fTIdGqkaM4N0kQKBgQDjqmybPvK+cfOK27Ql\nEk6aO0tuEth+2fZdfom41pKQKwzyjWgvtbM7efwEAKVjbV/2eVVqCWyr5CsuJ8V7\n4EcEx0xyZ+jnJuxwpeKb3uap6vZ6Uqlqwc9M5xuM6OpvL3hCD4Q/cOIRlnfOiowB\nMMRnTugP0GEwoe0+6vVBsZPk5wKBgQDQlZjf2P2CUaH/8fF4FLNsq/Gx06JziCvx\nY7wP0t103NE9iSbsH0vrS74BxVZaBrZe4pRVq2GlG+a8OQmIrkEpOKOvgvmZePAL\n9cWb8pT2DvW0St8nZ781PnBj0kJdfOYMeKDSvitjeCLhaftw4tXxVeWJRdYJvmPa\neUWya8+euQKBgDtjkcFNwnT7tNRcS7n/9JE3No4YTHGK2kG5aKPXFZSWJmJ/kNYh\nSAgT4jIJGRu+xHG/ZRCsNLUCaGE57sJJ1zzf4IlXoeHqvXMFUv/mPbXFnVZ7icZX\nmIQvJXi0qbMnZu+UrKMAB3kfD1HJwovs9M8ePlshuLi/BOyMzj283R+xAoGAT7Ub\nGk99cNNpKLl4IP8oIZsDXYUWSzf6MuB9+T4HSqUngHBs40aCaIQlM+AJQ5XVEyVa\nsfIv+jxTTBIvxn9wfyQZyPQwFanVcXyV6yYkLnNBCYijBhfrRZl6sWqj2b3k21ct\nfdsWEYjTK3iokVbdkr/UW0TIqiiLV+2H6MqyKGECgYEA2RJhASQ83oIx3qhQ/dgM\nSOtxZlrooWVWDpiaVjk7qLT5cszyynuqvY3mL+voC1Ak3tZvifZMiGZt1oFLR8nN\nV1GRZUuCxxV/GVQOsUji6UAibkm8FxCLZiQ7FKbjlIrvHzJmwH6C5yjr0PEDUbNn\nVsSMoFvFP7YvPBOfsEDjd3M=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "test/web-platform-tests/runner/certs/web-platform.test.pem",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number: 633812 (0x9abd4)\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: CN=web-platform-tests\n        Validity\n            Not Before: Aug 29 20:32:30 2025 GMT\n            Not After : Aug 29 20:32:30 2026 GMT\n        Subject: CN=web-platform.test\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                Public-Key: (2048 bit)\n                Modulus:\n                    00:b9:7f:82:63:9c:6a:e5:96:db:6a:26:18:61:b2:\n                    d2:8e:22:3e:42:0a:5b:43:6e:2b:53:67:19:ab:c8:\n                    b8:41:f3:62:95:f5:cb:ca:b2:15:d8:45:53:c4:21:\n                    de:cf:32:81:44:5c:77:10:6f:49:3e:fe:6f:8a:74:\n                    db:51:60:1e:87:60:9e:f6:c0:87:32:7d:b1:8b:06:\n                    ed:f2:c6:ec:79:98:e8:d1:2e:13:0d:38:06:3f:21:\n                    44:5f:58:5c:c5:03:a0:e3:09:b8:9e:e2:6a:3f:d3:\n                    df:49:92:43:93:fc:99:85:c9:16:16:8d:3b:a4:20:\n                    dc:38:35:0f:5f:5e:30:e7:f8:ec:98:f6:01:00:d9:\n                    9d:eb:56:3e:11:a9:a8:20:29:95:2f:38:83:46:4e:\n                    43:21:bc:76:fe:4e:07:33:0b:0d:2b:a5:f8:cb:d6:\n                    b7:10:3e:f7:ba:87:fd:95:7c:ff:95:86:10:11:39:\n                    c5:66:10:d4:2d:6b:50:67:2b:9d:75:45:79:d3:01:\n                    5c:a3:46:5a:3c:58:bf:50:30:ec:04:f2:29:81:db:\n                    cf:9f:59:af:2c:53:28:30:13:05:75:db:4c:ab:e0:\n                    ab:6a:f8:02:fa:6d:20:3b:54:20:22:7d:3e:b0:3a:\n                    cc:1a:64:19:68:df:29:99:79:7d:37:9a:f3:3e:ef:\n                    fc:ef\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Basic Constraints: \n                CA:FALSE\n            X509v3 Subject Key Identifier: \n                71:99:3D:63:BA:72:60:12:8C:5B:55:3A:91:2E:FB:3B:63:B8:CA:1E\n            X509v3 Authority Key Identifier: \n                58:81:93:CD:04:22:93:E7:BC:25:F6:3C:FD:98:5B:72:74:9A:C9:C3\n            X509v3 Key Usage: \n                Digital Signature, Non Repudiation, Key Encipherment\n            X509v3 Extended Key Usage: \n                TLS Web Server Authentication\n            X509v3 Subject Alternative Name: \n                DNS:web-platform.test, DNS:not-web-platform.test, DNS:www.web-platform.test, DNS:www1.web-platform.test, DNS:www2.web-platform.test, DNS:www.not-web-platform.test, DNS:www.www.web-platform.test, DNS:www2.www.web-platform.test, DNS:www1.www.web-platform.test, DNS:www2.not-web-platform.test, DNS:www.www2.web-platform.test, DNS:www.www1.web-platform.test, DNS:www1.not-web-platform.test, DNS:www2.www1.web-platform.test, DNS:www1.www1.web-platform.test, DNS:www2.www2.web-platform.test, DNS:www1.www2.web-platform.test, DNS:www.www.not-web-platform.test, DNS:www.www1.not-web-platform.test, DNS:www.www2.not-web-platform.test, DNS:www1.www.not-web-platform.test, DNS:xn--lve-6lad.web-platform.test, DNS:www2.www.not-web-platform.test, DNS:www2.www1.not-web-platform.test, DNS:www1.www2.not-web-platform.test, DNS:www1.www1.not-web-platform.test, DNS:www2.www2.not-web-platform.test, DNS:xn--lve-6lad.not-web-platform.test, DNS:www.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www.web-platform.test, DNS:www1.xn--lve-6lad.web-platform.test, DNS:www2.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www2.web-platform.test, DNS:xn--lve-6lad.www1.web-platform.test, DNS:xn--lve-6lad.www.not-web-platform.test, DNS:www.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www1.not-web-platform.test, DNS:www2.xn--lve-6lad.not-web-platform.test, DNS:www1.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www2.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.not-web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.not-web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test\n    Signature Algorithm: sha256WithRSAEncryption\n    Signature Value:\n        a2:5f:a1:fa:c2:84:f4:ca:49:4e:a8:9e:d8:98:7d:6d:c1:5a:\n        72:ab:90:d2:b9:81:b7:99:3e:3d:cb:db:37:17:9e:aa:b6:6c:\n        68:e7:16:ef:53:65:02:8c:98:19:ce:7d:aa:7b:e7:b2:89:e8:\n        8c:7b:35:01:3c:ab:d5:77:97:f6:e5:17:0b:7a:74:f9:6a:45:\n        38:13:77:75:51:60:22:fc:a3:8b:b9:a2:a8:ee:37:50:a7:a7:\n        96:bf:16:c7:00:f8:43:7c:bb:10:cd:04:0b:2e:5a:76:a5:c6:\n        fa:39:2e:2e:cf:45:0e:4f:62:9b:9c:80:36:a9:41:4a:cd:cd:\n        b3:f0:4e:8f:2e:96:79:fa:a2:90:32:cc:f7:40:9a:cc:a8:5d:\n        0a:a8:b1:8c:06:9c:bc:78:a4:e4:07:79:95:57:47:b9:54:75:\n        96:df:9d:13:39:56:ab:ee:97:51:86:c2:84:70:3c:e9:eb:79:\n        86:67:3d:e3:2b:7a:43:48:eb:73:e7:4b:29:57:7c:92:ea:42:\n        2e:68:34:14:11:54:65:0a:a2:82:d7:a8:82:17:05:aa:db:17:\n        3c:36:30:40:6a:88:5b:dd:4c:62:ce:66:80:25:35:15:8f:8e:\n        a6:29:d9:84:80:4f:2d:a0:a9:cc:e2:f1:fb:a3:c9:c2:9e:23:\n        08:91:e3:76\n-----BEGIN CERTIFICATE-----\nMIIMsjCCC5qgAwIBAgIDCavUMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl\nYi1wbGF0Zm9ybS10ZXN0czAeFw0yNTA4MjkyMDMyMzBaFw0yNjA4MjkyMDMyMzBa\nMBwxGjAYBgNVBAMMEXdlYi1wbGF0Zm9ybS50ZXN0MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAuX+CY5xq5ZbbaiYYYbLSjiI+QgpbQ24rU2cZq8i4QfNi\nlfXLyrIV2EVTxCHezzKBRFx3EG9JPv5vinTbUWAeh2Ce9sCHMn2xiwbt8sbseZjo\n0S4TDTgGPyFEX1hcxQOg4wm4nuJqP9PfSZJDk/yZhckWFo07pCDcODUPX14w5/js\nmPYBANmd61Y+EamoICmVLziDRk5DIbx2/k4HMwsNK6X4y9a3ED73uof9lXz/lYYQ\nETnFZhDULWtQZyuddUV50wFco0ZaPFi/UDDsBPIpgdvPn1mvLFMoMBMFddtMq+Cr\navgC+m0gO1QgIn0+sDrMGmQZaN8pmXl9N5rzPu/87wIDAQABo4IJ+jCCCfYwCQYD\nVR0TBAIwADAdBgNVHQ4EFgQUcZk9Y7pyYBKMW1U6kS77O2O4yh4wHwYDVR0jBBgw\nFoAUWIGTzQQik+e8JfY8/ZhbcnSaycMwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoG\nCCsGAQUFBwMBMIIJhQYDVR0RBIIJfDCCCXiCEXdlYi1wbGF0Zm9ybS50ZXN0ghVu\nb3Qtd2ViLXBsYXRmb3JtLnRlc3SCFXd3dy53ZWItcGxhdGZvcm0udGVzdIIWd3d3\nMS53ZWItcGxhdGZvcm0udGVzdIIWd3d3Mi53ZWItcGxhdGZvcm0udGVzdIIZd3d3\nLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lnd3dy53ZWItcGxhdGZvcm0udGVz\ndIIad3d3Mi53d3cud2ViLXBsYXRmb3JtLnRlc3SCGnd3dzEud3d3LndlYi1wbGF0\nZm9ybS50ZXN0ghp3d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIad3d3Lnd3dzIu\nd2ViLXBsYXRmb3JtLnRlc3SCGnd3dy53d3cxLndlYi1wbGF0Zm9ybS50ZXN0ghp3\nd3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIbd3d3Mi53d3cxLndlYi1wbGF0Zm9y\nbS50ZXN0ght3d3cxLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCG3d3dzIud3d3Mi53\nZWItcGxhdGZvcm0udGVzdIIbd3d3MS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0gh13\nd3cud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIed3d3Lnd3dzEubm90LXdlYi1w\nbGF0Zm9ybS50ZXN0gh53d3cud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCHnd3\ndzEud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIeeG4tLWx2ZS02bGFkLndlYi1w\nbGF0Zm9ybS50ZXN0gh53d3cyLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3\ndzIud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3Mi5ub3Qtd2Vi\nLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC\nH3d3dzIud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC5u\nb3Qtd2ViLXBsYXRmb3JtLnRlc3SCInd3dy54bi0tbHZlLTZsYWQud2ViLXBsYXRm\nb3JtLnRlc3SCInhuLS1sdmUtNmxhZC53d3cud2ViLXBsYXRmb3JtLnRlc3SCI3d3\ndzEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0giN3d3cyLnhuLS1sdmUt\nNmxhZC53ZWItcGxhdGZvcm0udGVzdIIjeG4tLWx2ZS02bGFkLnd3dzIud2ViLXBs\nYXRmb3JtLnRlc3SCI3huLS1sdmUtNmxhZC53d3cxLndlYi1wbGF0Zm9ybS50ZXN0\ngiZ4bi0tbHZlLTZsYWQud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIImd3d3Lnhu\nLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53\nd3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIInd3d3Mi54bi0tbHZlLTZsYWQubm90\nLXdlYi1wbGF0Zm9ybS50ZXN0gid3d3cxLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBs\nYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53d3cyLm5vdC13ZWItcGxhdGZvcm0u\ndGVzdIIpeG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SC\nK3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCLXhu\nLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dy53ZWItcGxhdGZvcm0udGVzdIItd3d3\nLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLndlYi1wbGF0Zm9ybS50ZXN0gi14bi0t\nbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1u\nOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzIu\neG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzEu\neG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1u\nOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCL3huLS1s\ndmUtNmxhZC54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF3d3cu\neG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF4\nbi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0\ngjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cyLm5vdC13ZWItcGxhdGZvcm0u\ndGVzdIIyeG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3MS5ub3Qtd2ViLXBsYXRm\nb3JtLnRlc3SCMnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1w\nbGF0Zm9ybS50ZXN0gjJ3d3cyLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLm5vdC13\nZWItcGxhdGZvcm0udGVzdII2eG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4tLWx2\nZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0gjZ4bi0tbHZlLTZsYWQueG4tLW44ajZk\nczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCOnhuLS1sdmUtNmxhZC54\nbi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCOnhu\nLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRm\nb3JtLnRlc3SCQXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1uOGo2ZHM1M2x3\nd2tycWh2MjhhLndlYi1wbGF0Zm9ybS50ZXN0gkV4bi0tbjhqNmRzNTNsd3drcnFo\ndjI4YS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRl\nc3QwDQYJKoZIhvcNAQELBQADggEBAKJfofrChPTKSU6ontiYfW3BWnKrkNK5gbeZ\nPj3L2zcXnqq2bGjnFu9TZQKMmBnOfap757KJ6Ix7NQE8q9V3l/blFwt6dPlqRTgT\nd3VRYCL8o4u5oqjuN1Cnp5a/FscA+EN8uxDNBAsuWnalxvo5Li7PRQ5PYpucgDap\nQUrNzbPwTo8ulnn6opAyzPdAmsyoXQqosYwGnLx4pOQHeZVXR7lUdZbfnRM5Vqvu\nl1GGwoRwPOnreYZnPeMrekNI63PnSylXfJLqQi5oNBQRVGUKooLXqIIXBarbFzw2\nMEBqiFvdTGLOZoAlNRWPjqYp2YSATy2gqczi8fujycKeIwiR43Y=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/web-platform-tests/runner/config.json",
    "content": "{\n  \"check_subdomains\": false,\n  \"ssl\": {\n    \"type\": \"pregenerated\",\n    \"pregenerated\": {\n      \"ca_cert_path\": \"../../../test/web-platform-tests/runner/certs/cacert.pem\",\n      \"host_cert_path\": \"../../../test/web-platform-tests/runner/certs/web-platform.test.pem\",\n      \"host_key_path\": \"../../../test/web-platform-tests/runner/certs/web-platform.test.key\"\n    }\n  }\n}\n"
  },
  {
    "path": "test/web-platform-tests/runner/test-runner.mjs",
    "content": "import {\n  fetch,\n  FormData,\n  Headers,\n  Request,\n  Response,\n  setGlobalOrigin,\n  CloseEvent,\n  WebSocket,\n  caches,\n  EventSource,\n  WebSocketStream,\n  WebSocketError\n} from '../../../index.js'\nimport { Cache } from '../../../lib/web/cache/cache.js'\nimport { CacheStorage } from '../../../lib/web/cache/cachestorage.js'\nimport { runInThisContext } from 'node:vm'\nimport { debuglog } from 'node:util'\n\nconst globalPropertyDescriptors = {\n  writable: true,\n  enumerable: false,\n  configurable: true\n}\n\nObject.defineProperties(globalThis, {\n  fetch: {\n    ...globalPropertyDescriptors,\n    enumerable: true,\n    value: fetch\n  },\n  FormData: {\n    ...globalPropertyDescriptors,\n    value: FormData\n  },\n  Headers: {\n    ...globalPropertyDescriptors,\n    value: Headers\n  },\n  Request: {\n    ...globalPropertyDescriptors,\n    value: Request\n  },\n  Response: {\n    ...globalPropertyDescriptors,\n    value: Response\n  },\n  WebSocket: {\n    ...globalPropertyDescriptors,\n    value: WebSocket\n  },\n  CloseEvent: {\n    ...globalPropertyDescriptors,\n    value: CloseEvent\n  },\n  caches: {\n    ...globalPropertyDescriptors,\n    value: caches\n  },\n  Cache: {\n    ...globalPropertyDescriptors,\n    value: Cache\n  },\n  CacheStorage: {\n    ...globalPropertyDescriptors,\n    value: CacheStorage\n  },\n  EventSource: {\n    ...globalPropertyDescriptors,\n    value: EventSource\n  },\n  WebSocketStream: {\n    ...globalPropertyDescriptors,\n    value: WebSocketStream\n  },\n  WebSocketError: {\n    ...globalPropertyDescriptors,\n    value: WebSocketError\n  }\n})\n\nconst log = debuglog('UNDICI_WPT')\nconst testUrl = process.argv[2]\n\n// Set up environment\nglobalThis.window = globalThis.self = globalThis\nglobalThis.location = new URL(testUrl)\nglobalThis.Window = Object.getPrototypeOf(globalThis).constructor\n\nsetGlobalOrigin(globalThis.location)\n\nfunction setupGlobalTestharnessCallbacks () {\n  globalThis.add_result_callback(({ message, name, stack, status }) => {\n    const data = JSON.stringify({ name, status, message, stack })\n    process.stdout.write(data + '\\n')\n  })\n\n  globalThis.add_completion_callback((tests, harnessStatus) => {\n    process.stdout.write('#$#$#' + JSON.stringify({ tests, harnessStatus }) + '\\n')\n    process.stdout._flush?.()\n\n    if (process.platform === 'win32') {\n      // https://github.com/nodejs/node/issues/56645#issuecomment-3077594952\n      setTimeout(() => {\n        // eslint-disable-next-line n/no-process-exit\n        process.exit(harnessStatus.status === 0 ? 0 : 1)\n      }, 50)\n    } else {\n      // eslint-disable-next-line n/no-process-exit\n      process.exit(harnessStatus.status === 0 ? 0 : 1)\n    }\n  })\n}\n\nprocess.on('uncaughtException', (reason) => {\n  process.stderr.write(`!#!#!#${JSON.stringify({ error: { stack: reason.stack, message: reason.message } })}\\n`)\n})\n\nprocess.on('unhandledRejection', (reason) => {\n  process.stderr.write(`!#!#!#${JSON.stringify({ error: { stack: reason.stack, message: reason.message } })}\\n`)\n})\n\nasync function generateAndRunBundle (url) {\n  const response = await fetch(url)\n  const body = await response.text()\n\n  // Scripts may have src tags without being enclosed in quotes. This matches both\n  // <script src=\"whatever\"></script> and\n  // <script src=whatever></script>\n  const scriptSrcRegex = /<script[^>]*src=\"?([^\"\\s>]*)\"?[^>]*><\\/script>/g\n  const inlineScriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\n\n  /** @type {{ url?: URL; content: string }[]} */\n  const scripts = []\n\n  let match\n  while ((match = scriptSrcRegex.exec(body)) !== null) {\n    const src = match[1]\n\n    try {\n      const scriptUrl = new URL(src, url)\n      log(`Loading script: ${scriptUrl}`)\n      const scriptResponse = await fetch(scriptUrl)\n      if (scriptResponse.ok) {\n        const scriptContent = await scriptResponse.text()\n        scripts.push({ url: scriptUrl, content: scriptContent })\n      }\n    } catch (error) {\n      console.warn(`Failed to load script: ${src}`)\n    }\n  }\n\n  // Extract inline scripts\n  while ((match = inlineScriptRegex.exec(body)) !== null) {\n    const scriptContent = match[1]\n    scripts.push({ content: scriptContent })\n  }\n\n  log(`Loaded ${scripts.length} scripts`)\n\n  // Execute all scripts in order\n  for (let i = 0; i < scripts.length; i++) {\n    log(`Executing script ${i + 1}/${scripts.length}`)\n    runInThisContext(scripts[i].content)\n\n    // Once the testharness is loaded, we want to setup the callbacks in case\n    // an error is thrown outside of the tests.\n    if (scripts[i].url?.pathname === '/resources/testharness.js') {\n      setupGlobalTestharnessCallbacks()\n    }\n  }\n\n  log('All scripts executed')\n}\n\ngenerateAndRunBundle(testUrl)\n"
  },
  {
    "path": "test/web-platform-tests/runner/utils.mjs",
    "content": "// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L3705\nexport function sanitizeUnpairedSurrogates (str) {\n  return str.replace(\n    /([\\ud800-\\udbff]+)(?![\\udc00-\\udfff])|(^|[^\\ud800-\\udbff])([\\udc00-\\udfff]+)/g,\n    function (_, low, prefix, high) {\n      let output = prefix || '' // Prefix may be undefined\n      const string = low || high // Only one of these alternates can match\n      for (let i = 0; i < string.length; i++) {\n        output += codeUnitStr(string[i])\n      }\n      return output\n    })\n}\n\nfunction codeUnitStr (char) {\n  return 'U+' + char.charCodeAt(0).toString(16)\n}\n\n/**\n * @type {import('../../../lib/util/promise')['createDeferredPromise']}\n */\nexport const createDeferredPromise =\n  Promise.withResolvers?.bind(Promise) ?? (await import('../../../lib/util/promise.js')).createDeferredPromise\n"
  },
  {
    "path": "test/web-platform-tests/wpt-runner.mjs",
    "content": "// Copyright 2018-2025 the Deno authors. MIT license.\n\nimport { spawn } from 'node:child_process'\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { debuglog } from 'node:util'\nimport {\n  sanitizeUnpairedSurrogates,\n  createDeferredPromise\n} from './runner/utils.mjs'\nimport * as jsondiffpatch from 'jsondiffpatch'\n\nconst WPT_DIR = join(import.meta.dirname, 'wpt')\nconst EXPECTATION_PATH = join(import.meta.dirname, 'expectation.json')\nconst CA_CERT_PATH = join(import.meta.dirname, 'runner/certs/cacert.pem')\n\nconst log = debuglog('UNDICI_WPT')\n\nasync function runWithTestUtil (testFunction) {\n  const { promise, resolve, reject } = createDeferredPromise()\n\n  console.log('Starting WPT server...')\n  const proc = spawn('python3', ['wpt', 'serve', '--config', '../runner/config.json'], {\n    cwd: WPT_DIR,\n    stdio: 'inherit'\n  })\n\n  proc.once('exit', () => resolve())\n  proc.once('error', (err) => reject(err))\n\n  const serverUrl = 'http://web-platform.test:8000/'\n\n  // Wait for server to be ready\n  while (true) {\n    await new Promise(resolve => setTimeout(resolve, 500))\n\n    try {\n      const req = await fetch(serverUrl) // eslint-disable-line no-restricted-globals\n      await req.body?.cancel()\n      if (req.status === 200) {\n        break\n      }\n    } catch (err) {\n      // Server not ready yet\n    }\n  }\n\n  console.log(`✅ WPT server started at ${serverUrl}`)\n\n  let results\n\n  try {\n    results = await testFunction()\n  } finally {\n    console.log('Killing WPT server')\n\n    if (!proc.killed) {\n      proc.kill('SIGINT')\n    }\n  }\n\n  await promise\n  return results\n}\n\nfunction runSingleTest (url, options, expectation, timeout = 10000) {\n  const startTime = Date.now()\n  const { promise, resolve, reject } = createDeferredPromise()\n\n  const proc = spawn('node', [\n    '--expose-gc',\n    '--no-warnings',\n    join(import.meta.dirname, 'runner/test-runner.mjs'),\n    url.toString()\n  ], {\n    stdio: ['ignore', 'pipe', 'pipe'],\n    env: {\n      ...process.env,\n      NO_COLOR: '1',\n      NODE_EXTRA_CA_CERTS: CA_CERT_PATH\n    }\n  })\n\n  const cases = []\n  let harnessStatus = null\n  let stdoutOutput = ''\n  let stderrOutput = ''\n  let error\n\n  const timer = setTimeout(() => {\n    if (!proc.killed) {\n      proc.kill('SIGINT')\n    }\n  }, timeout)\n\n  proc.stdout.setEncoding('utf-8')\n  proc.stdout.on('data', (chunk) => {\n    stdoutOutput += chunk\n\n    let delimiterIndex\n    while ((delimiterIndex = stdoutOutput.indexOf('#$#$#')) !== -1) {\n      const endIndex = stdoutOutput.indexOf('\\n', delimiterIndex)\n      if (endIndex !== -1) {\n        const message = stdoutOutput.slice(delimiterIndex + 5, endIndex)\n        try {\n          const { tests, harnessStatus: _harnessStatus } = JSON.parse(message)\n          harnessStatus = _harnessStatus\n          cases.push(...tests)\n        } catch (e) {\n          console.error('Failed to parse:', message)\n        }\n        stdoutOutput = stdoutOutput.slice(endIndex + 1)\n      } else {\n        break // Wait for more data\n      }\n    }\n  })\n\n  proc.stderr.setEncoding('utf-8')\n  proc.stderr.on('data', (chunk) => {\n    stderrOutput += chunk\n\n    let delimiterIndex\n    while ((delimiterIndex = stderrOutput.indexOf('!#!#!#')) !== -1) {\n      const endIndex = stderrOutput.indexOf('\\n', delimiterIndex)\n      if (endIndex !== -1) {\n        const message = stderrOutput.slice(delimiterIndex + 6, endIndex)\n        ;({ error } = JSON.parse(message))\n        stderrOutput = stderrOutput.slice(endIndex + 1)\n      } else {\n        break // Wait for more data\n      }\n    }\n  })\n\n  proc.once('exit', () => {\n    clearTimeout(timer)\n    const duration = Date.now() - startTime\n\n    resolve({\n      status: harnessStatus?.status ?? 1,\n      harnessStatus,\n      duration,\n      cases,\n      error\n    })\n  })\n\n  proc.once('error', (err) => reject(err))\n\n  return promise\n}\n\nfunction getExpectation () {\n  return JSON.parse(readFileSync(EXPECTATION_PATH, 'utf8'))\n}\n\nfunction updateExpectations (results) {\n  const expectations = getExpectation()\n\n  for (const { test, result } of results) {\n    const pathSegments = test.path.slice(1).split('/')\n    const filename = pathSegments.pop()\n\n    // Navigate to the correct nested object\n    let current = expectations\n    for (const segment of pathSegments) {\n      current[segment] ??= {}\n      current = current[segment]\n    }\n\n    // Set the expectation based on test result\n    const currentFilename = current[filename]\n\n    current[filename] = {\n      success: typeof currentFilename?.success === 'string'\n        ? currentFilename.success\n        : result.status === 0, // If test file itself did not error\n      cases: result.cases.map((c) => {\n        const currentCase = current[filename]?.cases.find((cc) => cc.name === c.name)\n\n        if (currentCase?.flaky) {\n          return {\n            name: c.name,\n            flaky: true\n          }\n        }\n\n        return {\n          name: c.name,\n          success: c.status === 0,\n          message: c.message ?? undefined\n        }\n      })\n    }\n  }\n\n  writeFileSync(EXPECTATION_PATH, JSON.stringify(expectations, null, 2) + '\\n')\n  console.log(`✅ Updated expectations file: ${EXPECTATION_PATH}`)\n}\n\nfunction getManifest () {\n  const manifestPath = join(WPT_DIR, 'MANIFEST.json')\n  if (!existsSync(manifestPath)) {\n    throw new Error('MANIFEST.json not found. Run setup first.')\n  }\n  return JSON.parse(readFileSync(manifestPath, 'utf8'))\n}\n\nfunction discoverTestsToRun (filter, expectation) {\n  const manifest = getManifest()\n  const tests = []\n\n  function walkManifest (folder, parentExpectation, prefix) {\n    for (const [key, entry] of Object.entries(folder)) {\n      if (Array.isArray(entry)) {\n        // Test file\n        for (const [path, options] of entry.slice(1)) {\n          if (!key.endsWith('.html') && !key.endsWith('.js')) continue\n\n          const testPath = path || `${prefix}/${key}`\n          const url = new URL(testPath, 'http://web-platform.test:8000')\n\n          if (url.pathname.includes('.worker.') ||\n              url.pathname.includes('serviceworker') ||\n              url.pathname.includes('sharedworker') ||\n              url.pathname.includes('shadowrealm')) {\n            continue\n          }\n\n          const finalPath = url.pathname + url.search\n          if (!filter.some((filter) => finalPath.startsWith(filter) || finalPath.slice(1).startsWith(filter))) {\n            continue\n          }\n\n          const pathSegments = finalPath.slice(1).split('/')\n\n          const filename = pathSegments[pathSegments.length - 1]\n          const testExpectation = parentExpectation?.[filename]\n\n          tests.push({\n            path: finalPath,\n            url,\n            options: options || { script_metadata: [] },\n            expectation: testExpectation\n          })\n        }\n      } else {\n        const folderExpectation =\n          Array.isArray(parentExpectation) || typeof parentExpectation === 'boolean'\n            ? parentExpectation\n            : parentExpectation?.[key]\n\n        walkManifest(entry, folderExpectation, `${prefix}/${key}`)\n      }\n    }\n  }\n\n  if (manifest.items?.testharness) {\n    walkManifest(manifest.items.testharness, expectation, '')\n  }\n\n  return tests\n}\n\nfunction generateWPTReport (results, startTime, endTime) {\n  const reportResults = []\n\n  for (const { test, result } of results) {\n    const status = result.status !== 0\n      ? 'CRASH'\n      : result.harnessStatus?.status === 0\n        ? 'OK'\n        : 'ERROR'\n\n    const message = result.harnessStatus?.message ?? result.error?.message ?? null\n\n    const reportResult = {\n      test: test.path,\n      subtests: result.cases.map((c) => {\n        let expected\n        if (c.status !== 0) {\n          const { success, cases } = test.expectation ?? {}\n          if (success === false) { // If the test failed to load\n            expected = 'FAIL'\n          } else if (Array.isArray(cases)) {\n            const theCase = cases.find((aCase) => aCase.name === c.name)\n            expected = theCase && !theCase.success ? 'FAIL' : 'PASS'\n          }\n        }\n\n        return {\n          name: sanitizeUnpairedSurrogates(c.name),\n          status: c.status === 0 ? 'PASS' : 'FAIL',\n          message: c.message ? sanitizeUnpairedSurrogates(c.message) : null,\n          expected,\n          known_intermittent: []\n        }\n      }),\n      status,\n      message: message ? sanitizeUnpairedSurrogates(message) : null,\n      duration: result.duration,\n      expected: status === 'OK' ? undefined : 'OK',\n      known_intermittent: []\n    }\n\n    reportResults.push(reportResult)\n  }\n\n  return {\n    time_start: startTime,\n    time_end: endTime,\n    results: reportResults\n  }\n}\n\nasync function setup () {\n  console.log('Setting up WPT environment...')\n\n  // Check Python\n  const pythonCheck = spawn('python3', ['--version'], { stdio: 'pipe' })\n  pythonCheck.stdout.setEncoding('ascii')\n  const pythonVersion = await new Promise((resolve, reject) => {\n    pythonCheck.stdout.on('data', (c) => {\n      const versionRegex = /^Python (?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)/.exec(c.trim())\n\n      if (versionRegex !== null) {\n        const { major, minor, patch } = versionRegex.groups\n        resolve({ major: Number(major), minor: Number(minor), patch: Number(patch) })\n        clearTimeout(timeout)\n      }\n    })\n\n    const timeout = setTimeout(reject, 30_000, 'Took too long to determine Python version')\n  })\n\n  if (pythonVersion.major !== 3) {\n    throw new Error('Python 3 is required')\n  }\n\n  // Check if manifest exists\n  const manifestPath = join(WPT_DIR, 'MANIFEST.json')\n  if (!existsSync(manifestPath)) {\n    console.log('Updating WPT manifest...')\n    const manifestProc = spawn('python3', ['wpt', 'manifest'], {\n      cwd: WPT_DIR,\n      stdio: 'inherit'\n    })\n    const manifestOk = await new Promise(resolve => {\n      manifestProc.on('exit', code => resolve(code === 0))\n      manifestProc.on('error', () => resolve(false))\n    })\n\n    if (!manifestOk) {\n      throw new Error('Failed to update manifest')\n    }\n  } else {\n    console.log('Using existing WPT manifest')\n  }\n\n  // Configure hosts file\n  const hostsPath = process.platform === 'win32'\n    ? `${process.env.SystemRoot}\\\\System32\\\\drivers\\\\etc\\\\hosts`\n    : '/etc/hosts'\n\n  const hostsContent = existsSync(hostsPath) ? readFileSync(hostsPath, 'utf8') : ''\n  const etcHostsConfigured = hostsContent.includes('web-platform.test')\n\n  async function setupHostsFile () {\n    const makeHostsProc = spawn('python3', ['wpt', 'make-hosts-file'], {\n      cwd: WPT_DIR,\n      stdio: ['ignore', 'pipe', 'pipe']\n    })\n\n    let stdout = ''\n    makeHostsProc.stdout.setEncoding('utf-8')\n    makeHostsProc.stdout.on('data', (data) => stdout += data) // eslint-disable-line no-return-assign\n\n    const success = await new Promise(resolve => {\n      makeHostsProc.on('exit', code => resolve(code === 0))\n    })\n\n    if (success) {\n      try {\n        const entries = '\\n\\n# Configured for Web Platform Tests (Node.js)\\n' + stdout\n        writeFileSync(hostsPath, entries)\n        console.log(`Updated ${hostsPath}`)\n      } catch (err) {\n        console.error(`Failed to write to ${hostsPath}. Please run with sudo or configure manually.`)\n        throw err\n      }\n    } else {\n      throw new Error('Failed to generate hosts entries')\n    }\n  }\n\n  if (etcHostsConfigured) {\n    console.log(hostsPath + ' is already configured.')\n  } else if (!process.env.CI) {\n    const rl = createInterface({\n      input: process.stdin,\n      output: process.stdout\n    })\n\n    /** @type {Promise<string>} */\n    const answer = await new Promise((resolve) => {\n      rl.question(\n        `The WPT require certain entries to be present in your ${hostsPath} file. Should these be configured automatically? (y/n): `,\n        resolve\n      )\n    }).finally(() => rl.close()).then((a) => a.trim().toLowerCase())\n\n    let hostsModified = false\n    if (answer === 'y' || answer === 'yes') {\n      try {\n        await setupHostsFile()\n        hostsModified = true\n      } catch (err) {\n        console.error('❌ \\x1B[31mAutomatic configuration failed.\\x1B[0m')\n      }\n    }\n    if (!hostsModified) {\n      console.log('Please configure hosts file manually:')\n      console.log(`cd ${WPT_DIR}`)\n      if (process.platform === 'win32') {\n        console.log('python wpt make-hosts-file | Out-File $env:SystemRoot\\\\System32\\\\drivers\\\\etc\\\\hosts -Encoding ascii -Append')\n      } else {\n        console.log('python3 wpt make-hosts-file | sudo tee -a /etc/hosts')\n      }\n\n      console.log('❌ \\x1B[31mSetup incomplete.\\x1B[0m')\n      process.exit(1) // eslint-disable-line n/no-process-exit\n    }\n  }\n\n  console.log('✅ Setup complete!')\n}\n\nasync function run (filters = []) {\n  const startTime = Date.now()\n  const expectation = getExpectation()\n  const tests = discoverTestsToRun(filters, expectation)\n\n  console.log(`Going to run ${tests.length} test files`)\n\n  const results = await runWithTestUtil(async () => {\n    const testResults = []\n\n    for (const test of tests) {\n      console.log(`${'='.repeat(40)}\\n${test.path}\\n`)\n\n      const timeout = test.options.timeout === 'long' ? 60_000 : 10_000\n      const result = await runSingleTest(test.url, test.options, test.expectation, timeout)\n\n      testResults.push({ test, result })\n\n      console.log(`${test.path}: ${result.cases.length} tests ran in ${result.duration}ms:`)\n\n      if (result.cases.length === 0) {\n        console.log(`\\t??. ❌ ${result.error?.message ?? 'N/A'}`)\n      }\n\n      for (const c of result.cases) {\n        console.log(`\\t${c.index + 1}. \"${c.name}\": ${c.status === 0 ? '✅ PASS' : '❌ FAIL'}`)\n\n        if (c.status !== 0 && (c.message || c.stack)) {\n          log(`${c.message}:\\n${c.stack.split('\\n').slice(1).join('\\n')}`)\n        }\n      }\n    }\n\n    return testResults\n  })\n\n  const endTime = Date.now()\n  console.log(`\\nCompleted in ${endTime - startTime}ms`)\n\n  // Calculate summary\n  const totalTests = results.length\n  const { pass, fail } = results.reduce((curr, { result }) => {\n    for (const c of result.cases) {\n      if (c.status !== 0) {\n        curr.fail++\n      } else {\n        curr.pass++\n      }\n    }\n\n    return curr\n  }, { pass: 0, fail: 0 })\n\n  console.log('\\n' + '='.repeat(50))\n  console.log('TEST SUMMARY')\n  console.log('='.repeat(50))\n  console.log(`Total Test Files: ${totalTests}`)\n  console.log(`✅ Passing: ${pass}`)\n  console.log(`❌ Failing: ${fail}`)\n  console.log('='.repeat(50))\n\n  if (process.env.WPT_REPORT) {\n    const report = generateWPTReport(results, startTime, endTime)\n    writeFileSync(process.env.WPT_REPORT, JSON.stringify(report))\n  } else {\n    const oldExpectations = getExpectation()\n    updateExpectations(results)\n\n    const jsondiff = jsondiffpatch.create({\n      propertyFilter: (name) => {\n        return name === 'success'\n      }\n    })\n\n    const diff = jsondiff.diff(oldExpectations, getExpectation())\n    process.exitCode = diff === undefined ? 0 : 1\n\n    if (diff !== undefined) {\n      console.dir(diff, { depth: Infinity })\n    }\n  }\n}\n\n// CLI\nconst command = process.argv[2]\nconst filters = process.argv.slice(3)\n\nswitch (command) {\n  case 'setup':\n    await setup()\n    break\n  case 'run':\n    await run(filters)\n    break\n  default:\n    console.log(`\nWPT Test Runner for Node.js\n\nCommands:\n  setup                    Configure environment\n  run [filter...]          Run tests\n`)\n    break\n}\n"
  },
  {
    "path": "test/webidl/converters.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst { webidl } = require('../../lib/web/webidl')\n\ntest('sequence', () => {\n  const converter = webidl.sequenceConverter(\n    webidl.converters.DOMString\n  )\n\n  assert.deepStrictEqual(converter([1, 2, 3]), ['1', '2', '3'])\n\n  assert.throws(() => {\n    converter(3, 'converter', 'converter')\n  }, TypeError, 'disallows non-objects')\n\n  assert.throws(() => {\n    converter(null, 'converter', 'converter')\n  }, TypeError)\n\n  assert.throws(() => {\n    converter(undefined, 'converter', 'converter')\n  }, TypeError)\n\n  assert.throws(() => {\n    converter({}, 'converter', 'converter')\n  }, TypeError, 'no Symbol.iterator')\n\n  assert.throws(() => {\n    converter({\n      [Symbol.iterator]: 42\n    })\n  }, TypeError, 'invalid Symbol.iterator')\n\n  assert.throws(() => {\n    converter(webidl.converters.sequence({\n      [Symbol.iterator] () {\n        return {\n          next: 'never!'\n        }\n      }\n    }), 'converter', 'converter')\n  }, TypeError, 'invalid generator')\n})\n\ndescribe('webidl.dictionaryConverter', () => {\n  test('arguments', () => {\n    const converter = webidl.dictionaryConverter([])\n\n    assert.throws(() => {\n      converter(true, 'converter', 'converter')\n    }, TypeError)\n\n    for (const value of [{}, undefined, null]) {\n      assert.doesNotThrow(() => {\n        converter(value, 'converter', 'converter')\n      })\n    }\n  })\n\n  test('required key', () => {\n    const converter = webidl.dictionaryConverter([\n      {\n        converter: () => true,\n        key: 'Key',\n        required: true\n      }\n    ])\n\n    assert.throws(() => {\n      converter({ wrongKey: 'key' }, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      converter({ Key: 'this key was required!' }, 'converter', 'converter')\n    })\n  })\n\n  test('null and undefined still populates defaultValue(s)', () => {\n    const dict = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters.any,\n        defaultValue: () => 3\n      }\n    ])\n\n    assert.deepStrictEqual(dict(null), { key: 3 })\n    assert.deepStrictEqual(dict(undefined), { key: 3 })\n  })\n\n  test('null and undefined throw a webidl TypeError with a required key', () => {\n    const dict = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters.any,\n        required: true\n      }\n    ])\n\n    assert.throws(() => dict(null, 'prefix'), new TypeError('prefix: Missing required key \"key\".'))\n    assert.throws(() => dict(undefined, 'prefix'), new TypeError('prefix: Missing required key \"key\".'))\n  })\n\n  test('Object type works for functions and regex (etc.)', () => {\n    const dict = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters.any,\n        required: true\n      }\n    ])\n\n    function obj () {}\n    obj.key = 1\n\n    const obj2 = / /\n    obj2.key = 1\n\n    assert.deepStrictEqual(dict(obj), { key: 1 })\n    assert.deepStrictEqual(dict(obj2), { key: 1 })\n  })\n\n  test('keys are accessed in lexicographical order', () => {\n    const converter = webidl.dictionaryConverter([\n      {\n        converter: () => true,\n        key: 'zyx'\n      },\n      {\n        converter: () => true,\n        key: 'abc'\n      }\n    ])\n\n    const accessed = []\n\n    converter({\n      get abc () {\n        return accessed.push('abc')\n      },\n      get zyx () {\n        return accessed.push('zyx')\n      }\n    }, 'converter', 'converter')\n\n    assert.deepStrictEqual(accessed, ['abc', 'zyx'])\n  })\n})\n\ndescribe('buffer source converters', () => {\n  test('ArrayBuffer', () => {\n    assert.throws(() => {\n      webidl.converters.ArrayBuffer(true, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.ArrayBuffer({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.ArrayBuffer(new ArrayBuffer(8), 'converter', 'converter')\n    })\n\n    assert.throws(() => {\n      webidl.converters.ArrayBuffer(new SharedArrayBuffer(64), 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.ArrayBuffer(\n        new ArrayBuffer(16, { maxByteLength: 64 }),\n        'converter',\n        'converter'\n      )\n    })\n\n    assert.doesNotThrow(() => {\n      webidl.converters.ArrayBuffer(\n        new ArrayBuffer(16, { maxByteLength: 64 }),\n        'converter',\n        'converter',\n        webidl.attributes.AllowResizable\n      )\n    })\n  })\n\n  test('SharedArrayBuffer', () => {\n    assert.throws(() => {\n      webidl.converters.SharedArrayBuffer(true, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.SharedArrayBuffer({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.SharedArrayBuffer(new SharedArrayBuffer(8), 'converter', 'converter')\n    })\n\n    assert.throws(() => {\n      webidl.converters.SharedArrayBuffer(new ArrayBuffer(64), 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.SharedArrayBuffer(\n        new SharedArrayBuffer(16, { maxByteLength: 64 }),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.SharedArrayBuffer(\n        new SharedArrayBuffer(16, { maxByteLength: 64 }),\n        'converter',\n        'converter',\n        webidl.attributes.AllowResizable\n      )\n    })\n  })\n\n  test('TypedArray', () => {\n    assert.throws(() => {\n      webidl.converters.TypedArray(3, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.TypedArray({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.TypedArray(new Uint8Array(), Uint8Array, 'converter', 'converter')\n    })\n\n    assert.throws(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new SharedArrayBuffer(16)),\n        Uint8Array,\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new SharedArrayBuffer(16)),\n        Uint8Array,\n        'converter',\n        'converter',\n        webidl.attributes.AllowShared\n      )\n    })\n\n    assert.throws(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new ArrayBuffer(16, { maxByteLength: 32 })),\n        Uint8Array,\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new ArrayBuffer(16, { maxByteLength: 32 })),\n        Uint8Array,\n        'converter',\n        'converter',\n        webidl.attributes.AllowResizable\n      )\n    })\n\n    assert.throws(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new SharedArrayBuffer(16, { maxByteLength: 32 })),\n        Uint8Array,\n        'converter',\n        'converter',\n        webidl.attributes.AllowResizable\n      )\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new SharedArrayBuffer(16, { maxByteLength: 32 })),\n        Uint8Array,\n        'converter',\n        'converter',\n        webidl.attributes.AllowShared\n      )\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.TypedArray(\n        new Uint8Array(new SharedArrayBuffer(16, { maxByteLength: 32 })),\n        Uint8Array,\n        'converter',\n        'converter',\n        webidl.attributes.AllowResizable | webidl.attributes.AllowShared\n      )\n    })\n  })\n\n  test('DataView', () => {\n    assert.throws(() => {\n      webidl.converters.DataView(3, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.DataView({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.DataView(new Uint8Array(), 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.DataView(new DataView(new ArrayBuffer(8)), 'converter', 'converter')\n    })\n\n    assert.throws(() => {\n      webidl.converters.DataView(\n        new DataView(new SharedArrayBuffer(16)),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.DataView(\n        new DataView(new ArrayBuffer(16, { maxByteLength: 64 })),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n  })\n\n  test('ArrayBufferView', () => {\n    assert.throws(() => {\n      webidl.converters.ArrayBufferView(3, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.ArrayBufferView({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.ArrayBufferView(new Uint8Array(), 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.ArrayBufferView(new DataView(new ArrayBuffer(8)), 'converter', 'converter')\n    })\n\n    assert.throws(() => {\n      webidl.converters.ArrayBufferView(\n        new Uint8Array(new SharedArrayBuffer(16)),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.ArrayBufferView(\n        new Float32Array(new ArrayBuffer(16, { maxByteLength: 64 })),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n  })\n\n  test('BufferSource', () => {\n    assert.throws(() => {\n      webidl.converters.BufferSource(3, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.BufferSource({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.BufferSource(new SharedArrayBuffer(16), 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.BufferSource(\n        new Uint8Array(new SharedArrayBuffer(16)),\n        'converter',\n        'converter'\n      )\n    }, TypeError)\n  })\n\n  test('AllowSharedBufferSource', () => {\n    assert.throws(() => {\n      webidl.converters.AllowSharedBufferSource(3, 'converter', 'converter')\n    }, TypeError)\n\n    assert.throws(() => {\n      webidl.converters.AllowSharedBufferSource({}, 'converter', 'converter')\n    }, TypeError)\n\n    assert.doesNotThrow(() => {\n      webidl.converters.AllowSharedBufferSource(new SharedArrayBuffer(16), 'converter', 'converter')\n    })\n\n    assert.doesNotThrow(() => {\n      webidl.converters.AllowSharedBufferSource(\n        new Uint8Array(new SharedArrayBuffer(16)),\n        'converter',\n        'converter'\n      )\n    })\n  })\n})\n\ntest('ByteString', () => {\n  assert.doesNotThrow(() => {\n    webidl.converters.ByteString('', 'converter', 'converter')\n  })\n\n  // https://github.com/nodejs/undici/issues/1590\n  assert.throws(() => {\n    const char = String.fromCharCode(256)\n    webidl.converters.ByteString(`invalid${char}char`, 'converter', 'converter')\n  }, {\n    message: 'Cannot convert argument to a ByteString because the character at ' +\n             'index 7 has a value of 256 which is greater than 255.'\n  })\n})\n\ntest('recordConverter', () => {\n  const anyConverter = webidl.recordConverter(webidl.converters.any, webidl.converters.any)\n\n  assert.throws(\n    () => anyConverter(null, 'prefix', 'argument'),\n    new TypeError('prefix: argument (\"Null\") is not an Object.')\n  )\n})\n\ntest('webidl.converters.boolean', () => {\n  assert.strictEqual(webidl.converters.boolean(null), false)\n  assert.strictEqual(webidl.converters.boolean(undefined), false)\n\n  assert.strictEqual(webidl.converters.boolean(true), true)\n  assert.strictEqual(webidl.converters.boolean(false), false)\n\n  assert.strictEqual(webidl.converters.boolean(''), false)\n  assert.strictEqual(webidl.converters.boolean('true'), true)\n  assert.strictEqual(webidl.converters.boolean('false'), true)\n\n  assert.strictEqual(webidl.converters.boolean(1), true)\n  assert.strictEqual(webidl.converters.boolean(0), false)\n  assert.strictEqual(webidl.converters.boolean(-0), false)\n  assert.strictEqual(webidl.converters.boolean(NaN), false)\n  assert.strictEqual(webidl.converters.boolean(Infinity), true)\n  assert.strictEqual(webidl.converters.boolean(-Infinity), true)\n\n  assert.strictEqual(webidl.converters.boolean(0n), false)\n  assert.strictEqual(webidl.converters.boolean(1n), true)\n\n  assert.strictEqual(webidl.converters.boolean({}), true)\n  assert.strictEqual(webidl.converters.boolean([]), true)\n  assert.strictEqual(webidl.converters.boolean(() => {}), true)\n  assert.strictEqual(webidl.converters.boolean(/a/), true)\n  assert.strictEqual(webidl.converters.boolean(new Date()), true)\n  assert.strictEqual(webidl.converters.boolean(new Map()), true)\n  assert.strictEqual(webidl.converters.boolean(new Set()), true)\n  assert.strictEqual(webidl.converters.boolean(new Date()), true)\n})\n"
  },
  {
    "path": "test/webidl/errors.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst assert = require('node:assert')\nconst { Headers, MessageEvent } = require('../..')\n\ntest('ByteString', (t) => {\n  const name = Symbol('')\n  const value = Symbol('')\n\n  for (const method of [\n    'get',\n    'set',\n    'delete',\n    'append',\n    'has'\n  ]) {\n    assert.throws(\n      () => new Headers()[method](name, value),\n      new TypeError(`Headers.${method}: name is a symbol, which cannot be converted to a ByteString.`)\n    )\n  }\n})\n\ndescribe('dictionary converters', () => {\n  test('error message retains property name', () => {\n    assert.throws(\n      () => new MessageEvent('message', { source: 1 }),\n      new TypeError('MessageEvent constructor: Expected eventInitDict.source (\"1\") to be an instance of MessagePort.')\n    )\n  })\n})\n\ndescribe('sequence converters', () => {\n  test('retains index', () => {\n    const { port1 } = new MessageChannel()\n\n    assert.throws(\n      () => new MessageEvent('type', { ports: [{}] }),\n      new TypeError('MessageEvent constructor: Expected eventInitDict.ports[0] (\"{}\") to be an instance of MessagePort.')\n    )\n\n    assert.throws(\n      () => new MessageEvent('type', { ports: [port1, {}] }),\n      new TypeError('MessageEvent constructor: Expected eventInitDict.ports[1] (\"{}\") to be an instance of MessagePort.')\n    )\n  })\n})\n"
  },
  {
    "path": "test/webidl/helpers.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst { webidl } = require('../../lib/web/webidl')\n\ntest('webidl.interfaceConverter', (t) => {\n  class A {}\n  class B {}\n\n  const converter = webidl.interfaceConverter(webidl.util.MakeTypeAssertion(A))\n\n  assert.throws(() => {\n    converter(new B(), 'converter', 'converter')\n  }, TypeError)\n\n  assert.doesNotThrow(() => {\n    converter(new A(), 'converter', 'converter')\n  })\n\n  t.test('interfaceConverters ignore Symbol.hasInstance', () => {\n    class V {}\n\n    Object.defineProperty(Blob.prototype, Symbol.hasInstance, {\n      value: () => true\n    })\n\n    const blobConverter = webidl.interfaceConverter(webidl.is.Blob, 'Blob')\n\n    assert.throws(() => blobConverter(new V()))\n    assert.equal(webidl.is.Blob(new V()), false)\n  })\n})\n\ndescribe('webidl.dictionaryConverter', () => {\n  test('extraneous keys are provided', () => {\n    const converter = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters.USVString,\n        defaultValue: 420,\n        required: true\n      }\n    ])\n\n    assert.deepStrictEqual(\n      converter({\n        a: 'b',\n        key: 'string',\n        c: 'd',\n        get value () {\n          return 6\n        }\n      }, 'converter', 'converter'),\n      { key: 'string' }\n    )\n  })\n\n  test('defaultValue with key = null', () => {\n    const converter = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters['unsigned short'],\n        defaultValue: 200\n      }\n    ])\n\n    assert.deepStrictEqual(converter({ key: null }, 'converter', 'converter'), { key: 0 })\n  })\n\n  test('no defaultValue and optional', () => {\n    const converter = webidl.dictionaryConverter([\n      {\n        key: 'key',\n        converter: webidl.converters.ByteString\n      }\n    ])\n\n    assert.deepStrictEqual(converter({ a: 'b', c: 'd' }, 'converter', 'converter'), {})\n  })\n})\n"
  },
  {
    "path": "test/webidl/util.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst assert = require('node:assert')\nconst { webidl } = require('../../lib/web/webidl')\n\ntest('webidl.util.Type(V)', () => {\n  const Type = webidl.util.Type\n  const Types = webidl.util.Types\n\n  assert.equal(Type(undefined), Types.UNDEFINED)\n  assert.equal(Type(null), Types.NULL)\n  assert.equal(Type(true), Types.BOOLEAN)\n  assert.equal(Type('string'), Types.STRING)\n  assert.equal(Type(Symbol('symbol')), Types.SYMBOL)\n  assert.equal(Type(1.23), Types.NUMBER)\n  assert.equal(Type(1n), Types.BIGINT)\n  assert.equal(Type({ a: 'b' }), Types.OBJECT)\n  assert.equal(Type(function () {}), Types.OBJECT)\n  assert.equal(Type([1, 2, 3]), Types.OBJECT)\n})\n\ntest('webidl.util.Stringify(V)', (t) => {\n  const circular = {}\n  circular.circular = circular\n\n  const pairs = [\n    [Object.create(null), '[Object: null prototype] {}'],\n    [{ a: 'b' }, \"{ a: 'b' }\"],\n    [[1, 2, 3], '[ 1, 2, 3 ]'],\n    [Symbol('sym'), 'Symbol(sym)'],\n    [Symbol.iterator, 'Symbol(Symbol.iterator)'], // well-known symbol\n    [true, 'true'],\n    [false, 'false'],\n    [1.23, '1.23'],\n    [Infinity, 'Infinity'],\n    [-Infinity, '-Infinity'],\n    [NaN, 'NaN'],\n    [0, '0'],\n    [1, '1'],\n    [1.5, '1.5'],\n    [1e10, '10000000000'],\n    [1e-10, '1e-10'],\n    [1e+10, '10000000000'],\n    [1e-10, '1e-10'],\n    [0, '0'],\n    [-0, '0'],\n    [1n, '1n'],\n    ['hello', '\"hello\"'],\n    ['', '\"\"'],\n    [function () {}, '[Function (anonymous)]'],\n    [function named () {}, '[Function: named]'],\n    [null, 'null'],\n    [undefined, 'undefined'],\n    [circular, '<ref *1> { circular: [Circular *1] }']\n  ]\n\n  for (const [value, expected] of pairs) {\n    assert.deepStrictEqual(webidl.util.Stringify(value), expected)\n  }\n})\n\ntest('webidl.util.ConvertToInt(V)', () => {\n  const ConvertToInt = webidl.util.ConvertToInt\n\n  assert.equal(ConvertToInt(63, 64, 'signed'), 63, 'odd int')\n  assert.equal(ConvertToInt(64.49, 64, 'signed'), 64)\n  assert.equal(ConvertToInt(64.51, 64, 'signed'), 64)\n\n  const max = 2 ** 53\n  assert.equal(ConvertToInt(max + 1, 64, 'signed'), max, 'signed pos')\n  assert.equal(ConvertToInt(-max - 1, 64, 'signed'), -max, 'signed neg')\n\n  assert.equal(ConvertToInt(max + 1, 64, 'unsigned'), max + 1, 'unsigned pos')\n  assert.equal(ConvertToInt(-max - 1, 64, 'unsigned'), -max - 1, 'unsigned neg')\n\n  for (const signedness of ['signed', 'unsigned']) {\n    assert.equal(ConvertToInt(Infinity, 64, signedness), 0)\n    assert.equal(ConvertToInt(-Infinity, 64, signedness), 0)\n    assert.equal(ConvertToInt(NaN, 64, signedness), 0)\n  }\n\n  for (const signedness of ['signed', 'unsigned']) {\n    assert.throws(() => {\n      ConvertToInt(NaN, 64, signedness, webidl.attributes.EnforceRange)\n    }, TypeError)\n\n    assert.throws(() => {\n      ConvertToInt(Infinity, 64, signedness, webidl.attributes.EnforceRange)\n    }, TypeError)\n\n    assert.throws(() => {\n      ConvertToInt(-Infinity, 64, signedness, webidl.attributes.EnforceRange)\n    }, TypeError)\n\n    assert.throws(() => {\n      ConvertToInt(2 ** 53 + 1, 32, 'signed', webidl.attributes.EnforceRange)\n    }, TypeError)\n\n    assert.throws(() => {\n      ConvertToInt(-(2 ** 53 + 1), 32, 'unsigned', webidl.attributes.EnforceRange)\n    }, TypeError)\n\n    assert.equal(\n      ConvertToInt(65.5, 64, signedness, webidl.attributes.EnforceRange),\n      65\n    )\n  }\n\n  for (const signedness of ['signed', 'unsigned']) {\n    assert.equal(\n      ConvertToInt(63.49, 64, signedness, webidl.attributes.Clamp),\n      64\n    )\n\n    assert.equal(\n      ConvertToInt(63.51, 64, signedness, webidl.attributes.Clamp),\n      64\n    )\n\n    assert.equal(\n      ConvertToInt(-0, 64, signedness, webidl.attributes.Clamp),\n      0\n    )\n  }\n\n  assert.equal(ConvertToInt(111, 2, 'signed'), -1)\n})\n"
  },
  {
    "path": "test/websocket/client-received-masked-frame.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst { WebsocketFrameSend } = require('../../lib/web/websocket/frame')\n\ntest('Client fails the connection if receiving a masked frame', async (t) => {\n  t.plan(2)\n\n  const body = Buffer.allocUnsafe(2)\n  body.writeUInt16BE(1006, 0)\n\n  const frame = new WebsocketFrameSend(body)\n  const buffer = frame.createFrame(0x8)\n\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    socket.write(buffer, () => ws.close())\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('close', (e) => {\n    t.assert.deepStrictEqual(e.code, 1006)\n  })\n\n  ws.addEventListener('error', () => {\n    t.assert.ok(true)\n  })\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/close-invalid-status-code.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Client fails the connection if receiving a masked frame', async (t) => {\n  t.plan(2)\n\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    // 1006 status code\n    socket.write(Buffer.from([0x88, 0x02, 0x03, 0xee]), () => ws.close())\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('close', (e) => {\n    t.assert.deepStrictEqual(e.code, 1006)\n  })\n\n  ws.addEventListener('error', () => {\n    t.assert.ok(true)\n  })\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/close-invalid-utf-8.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Receiving a close frame with invalid utf-8', (t, done) => {\n  t.plan(2)\n\n  const server = new WebSocketServer({ port: 0 })\n  t.after(() => {\n    server.close()\n  })\n\n  server.on('connection', (ws) => {\n    ws.close(1000, Buffer.from([0xFF, 0xFE]))\n\n    ws.on('close', (code) => {\n      t.assert.strictEqual(code, 1007)\n      done()\n    })\n  })\n\n  const events = []\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n  t.after(() => { ws.close() })\n\n  ws.addEventListener('close', (e) => {\n    events.push({ type: 'close', code: e.code })\n  })\n\n  ws.addEventListener('error', () => {\n    events.push({ type: 'error' })\n  })\n\n  once(ws, 'close').then(() => {\n    // An error event should be propagated immediately, then we should receive\n    // a close event with a 1006 code. The code is 1006, and not 1007 (as we send\n    // the server) because the connection is closed before the server responds.\n    t.assert.deepStrictEqual(events, [\n      { type: 'error' },\n      { type: 'close', code: 1006 }\n    ])\n  })\n})\n"
  },
  {
    "path": "test/websocket/close.js",
    "content": "'use strict'\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { describe, test, after } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ndescribe('Close', () => {\n  test('Close with code', (t) => {\n    return new Promise((resolve) => {\n      const server = new WebSocketServer({ port: 0 })\n\n      server.on('connection', (ws) => {\n        ws.on('close', (code) => {\n          t.assert.strictEqual(code, 1000)\n          server.close()\n          resolve()\n        })\n      })\n\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n      ws.addEventListener('open', () => ws.close(1000))\n    })\n  })\n\n  test('Close with code and reason', (t) => {\n    return new Promise((resolve) => {\n      const server = new WebSocketServer({ port: 0 })\n\n      server.on('connection', (ws) => {\n        ws.on('close', (code, reason) => {\n          t.assert.strictEqual(code, 1000)\n          t.assert.deepStrictEqual(reason, Buffer.from('Goodbye'))\n          server.close()\n          resolve()\n        })\n      })\n\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n      ws.addEventListener('open', () => ws.close(1000, 'Goodbye'))\n    })\n  })\n\n  test('Close with invalid code', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    return new Promise((resolve) => {\n      ws.addEventListener('open', () => {\n        t.assert.throws(\n          () => ws.close(2999),\n          {\n            name: 'InvalidAccessError',\n            constructor: DOMException\n          }\n        )\n\n        t.assert.throws(\n          () => ws.close(5000),\n          {\n            name: 'InvalidAccessError',\n            constructor: DOMException\n          }\n        )\n\n        ws.close()\n        server.close()\n        resolve()\n      })\n    })\n  })\n\n  test('Close with invalid reason', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    return new Promise((resolve) => {\n      ws.addEventListener('open', () => {\n        t.assert.throws(\n          () => ws.close(1000, 'a'.repeat(124)),\n          {\n            name: 'SyntaxError',\n            constructor: DOMException\n          }\n        )\n\n        ws.close(1000)\n        server.close()\n        resolve()\n      })\n    })\n  })\n\n  test('Close with no code or reason', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('close', (code, reason) => {\n          t.assert.strictEqual(code, 1005)\n          t.assert.deepStrictEqual(reason, Buffer.alloc(0))\n          server.close()\n          resolve()\n        })\n      })\n\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n      ws.addEventListener('open', () => ws.close())\n    })\n  })\n\n  test('Close with a 3000 status code', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('close', (code, reason) => {\n          t.assert.strictEqual(code, 3000)\n          t.assert.deepStrictEqual(reason, Buffer.alloc(0))\n          server.close()\n          resolve()\n        })\n      })\n\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n      ws.addEventListener('open', () => ws.close(3000))\n    })\n  })\n\n  test('calling close twice will only trigger the close event once', async (t) => {\n    t = tspl(t, { plan: 1 })\n\n    const server = new WebSocketServer({ port: 0 })\n\n    after(() => server.close())\n\n    server.on('connection', (ws) => {\n      ws.on('close', (code) => {\n        t.strictEqual(code, 1000)\n      })\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n    ws.addEventListener('open', () => {\n      ws.close(1000)\n      ws.close(1000)\n    })\n\n    await t.completed\n  })\n})\n"
  },
  {
    "path": "test/websocket/constructor.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\n\ntest('Constructor', (t) => {\n  t.assert.throws(\n    () => new WebSocket('abc'),\n    {\n      name: 'SyntaxError',\n      constructor: DOMException\n    }\n  )\n\n  t.assert.throws(\n    () => new WebSocket('wss://echo.websocket.events/#a'),\n    {\n      name: 'SyntaxError',\n      constructor: DOMException\n    }\n  )\n\n  t.assert.throws(\n    () => new WebSocket('wss://echo.websocket.events', ''),\n    {\n      name: 'SyntaxError',\n      constructor: DOMException\n    }\n  )\n\n  t.assert.throws(\n    () => new WebSocket('wss://echo.websocket.events', ['chat', 'chat']),\n    {\n      name: 'SyntaxError',\n      constructor: DOMException\n    }\n  )\n\n  t.assert.throws(\n    () => new WebSocket('wss://echo.websocket.events', ['<>@,;:\\\\\"/[]?={}\\t']),\n    {\n      name: 'SyntaxError',\n      constructor: DOMException\n    }\n  )\n})\n"
  },
  {
    "path": "test/websocket/continuation-frames.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Receiving multiple continuation frames works as expected', (t, done) => {\n  t.plan(1)\n\n  const frames = [\n    Buffer.from([0x01, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // text frame \"hello\" (fragmented)\n    Buffer.from([0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // continuation frame \"hello\" (fin clear)\n    Buffer.from([0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // continuation frame \"hello\" (fin clear)\n    Buffer.from([0x80, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]) // continuation frame \"hello\" (fin set)\n  ]\n\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    for (const frame of frames) {\n      socket.write(frame)\n    }\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.onerror = t.assert.fail\n  ws.onmessage = (e) => {\n    t.assert.deepStrictEqual(e.data, 'hellohellohellohello')\n    done()\n  }\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n})\n"
  },
  {
    "path": "test/websocket/custom-headers.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { Agent, WebSocket } = require('../..')\n\ntest('Setting custom headers', (t, done) => {\n  const headers = {\n    'x-khafra-hello': 'hi',\n    Authorization: 'Bearer base64orsomethingitreallydoesntmatter'\n  }\n\n  class TestAgent extends Agent {\n    dispatch (options) {\n      t.assert.deepStrictEqual(options.headers['x-khafra-hello'], headers['x-khafra-hello'])\n      t.assert.deepStrictEqual(options.headers.Authorization, headers.Authorization)\n      done()\n      return false\n    }\n  }\n\n  const ws = new WebSocket('wss://echo.websocket.events', {\n    headers,\n    dispatcher: new TestAgent()\n  })\n\n  ws.onclose = ws.onerror = ws.onmessage = t.assert.fail\n})\n"
  },
  {
    "path": "test/websocket/diagnostics-channel-handshake-response.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst dc = require('node:diagnostics_channel')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('diagnostics channel - undici:websocket:open includes handshake response', async (t) => {\n  t.plan(11)\n\n  const server = new WebSocketServer({ port: 0 })\n  const { port } = server.address()\n\n  server.on('connection', (ws) => {\n    setTimeout(() => {\n      ws.close(1000, 'test')\n    }, 50)\n  })\n\n  const openListener = (data) => {\n    // Verify handshake response data\n    t.assert.ok(data.handshakeResponse, 'handshakeResponse should be defined')\n    t.assert.strictEqual(data.handshakeResponse.status, 101, 'status should be 101')\n    t.assert.strictEqual(data.handshakeResponse.statusText, 'Switching Protocols', 'statusText should be correct')\n    // Check handshake response headers\n    const headers = data.handshakeResponse.headers\n    t.assert.ok(headers, 'headers should be defined')\n    t.assert.ok(typeof headers === 'object', 'headers should be an object')\n    t.assert.ok('upgrade' in headers, 'upgrade header should be present')\n    t.assert.ok('connection' in headers, 'connection header should be present')\n    t.assert.ok('sec-websocket-accept' in headers, 'sec-websocket-accept header should be present')\n    // Optionally, check values\n    t.assert.strictEqual(headers.upgrade.toLowerCase(), 'websocket', 'upgrade header should be websocket')\n    t.assert.strictEqual(headers.connection.toLowerCase(), 'upgrade', 'connection header should be upgrade')\n    t.assert.ok(typeof headers['sec-websocket-accept'] === 'string', 'sec-websocket-accept header should be a string')\n  }\n\n  dc.channel('undici:websocket:open').subscribe(openListener)\n\n  t.after(() => {\n    server.close()\n    dc.channel('undici:websocket:open').unsubscribe(openListener)\n  })\n\n  // Create WebSocket connection\n\n  const _ws = new WebSocket(`ws://localhost:${port}`)\n\n  await once(_ws, 'open')\n})\n"
  },
  {
    "path": "test/websocket/diagnostics-channel-open-close.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst dc = require('node:diagnostics_channel')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('diagnostics channel - undici:websocket:[open/close]', async (t) => {\n  t.plan(6)\n\n  const server = new WebSocketServer({ port: 0 })\n  const { port } = server.address()\n  const ws = new WebSocket(`ws://localhost:${port}`, 'chat')\n\n  server.on('connection', (ws) => {\n    ws.close(1000, 'goodbye')\n  })\n\n  const openListener = ({ extensions, protocol, websocket }) => {\n    t.assert.strictEqual(extensions, '')\n    t.assert.strictEqual(protocol, 'chat')\n    t.assert.strictEqual(websocket, ws)\n  }\n\n  const closeListener = ({ websocket, code, reason }) => {\n    t.assert.strictEqual(code, 1000)\n    t.assert.strictEqual(reason, 'goodbye')\n    t.assert.strictEqual(websocket, ws)\n  }\n\n  dc.channel('undici:websocket:open').subscribe(openListener)\n  dc.channel('undici:websocket:close').subscribe(closeListener)\n\n  t.after(() => {\n    server.close()\n    dc.channel('undici:websocket:open').unsubscribe(openListener)\n    dc.channel('undici:websocket:close').unsubscribe(closeListener)\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/diagnostics-channel-ping-pong.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst dc = require('node:diagnostics_channel')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('diagnostics channel - undici:websocket:[ping/pong]', async (t) => {\n  t.plan(4)\n\n  const server = new WebSocketServer({ port: 0 })\n  const { port } = server.address()\n  const ws = new WebSocket(`ws://localhost:${port}`, 'chat')\n\n  server.on('connection', (ws) => {\n    ws.ping('Ping')\n    ws.pong('Pong')\n    ws.close()\n  })\n\n  const pingListener = ({ websocket, payload }) => {\n    t.assert.strictEqual(websocket, ws)\n    t.assert.deepStrictEqual(payload, Buffer.from('Ping'))\n  }\n\n  const pongListener = ({ websocket, payload }) => {\n    t.assert.strictEqual(websocket, ws)\n    t.assert.deepStrictEqual(payload, Buffer.from('Pong'))\n  }\n\n  dc.channel('undici:websocket:ping').subscribe(pingListener)\n  dc.channel('undici:websocket:pong').subscribe(pongListener)\n\n  t.after(() => {\n    server.close()\n    dc.channel('undici:websocket:ping').unsubscribe(pingListener)\n    dc.channel('undici:websocket:pong').unsubscribe(pongListener)\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/events.js",
    "content": "'use strict'\n\nconst { test, describe, after } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { MessageEvent, CloseEvent, ErrorEvent } = require('../../lib/web/websocket/events')\nconst { WebSocket } = require('../..')\n\ntest('MessageEvent', (t) => {\n  t.assert.throws(() => new MessageEvent(), TypeError, 'no arguments')\n  t.assert.throws(() => new MessageEvent('').initMessageEvent(), TypeError)\n\n  const noInitEvent = new MessageEvent('message')\n\n  t.assert.strictEqual(noInitEvent.origin, '')\n  t.assert.strictEqual(noInitEvent.data, null)\n  t.assert.strictEqual(noInitEvent.lastEventId, '')\n  t.assert.strictEqual(noInitEvent.source, null)\n  t.assert.ok(Array.isArray(noInitEvent.ports))\n  t.assert.ok(Object.isFrozen(noInitEvent.ports))\n  t.assert.ok(new MessageEvent('').initMessageEvent('message') instanceof MessageEvent)\n})\n\ntest('CloseEvent', (t) => {\n  t.assert.throws(() => new CloseEvent(), TypeError)\n\n  const noInitEvent = new CloseEvent('close')\n\n  t.assert.strictEqual(noInitEvent.wasClean, false)\n  t.assert.strictEqual(noInitEvent.code, 0)\n  t.assert.strictEqual(noInitEvent.reason, '')\n})\n\ntest('ErrorEvent', (t) => {\n  t.assert.throws(() => new ErrorEvent(), TypeError)\n\n  const noInitEvent = new ErrorEvent('error')\n\n  t.assert.strictEqual(noInitEvent.message, '')\n  t.assert.strictEqual(noInitEvent.filename, '')\n  t.assert.strictEqual(noInitEvent.lineno, 0)\n  t.assert.strictEqual(noInitEvent.colno, 0)\n  t.assert.strictEqual(noInitEvent.error, undefined)\n})\n\ndescribe('Event handlers', () => {\n  const server = new WebSocketServer({ port: 0 })\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  after(() => {\n    server.close()\n    ws.close()\n  })\n\n  function listen () {}\n\n  describe('onopen', () => {\n    test('should be null initially', (t) => {\n      t.assert.strictEqual(ws.onopen, null)\n    })\n\n    test('should not allow non-function assignments', (t) => {\n      ws.onopen = 3\n      t.assert.strictEqual(ws.onopen, null)\n    })\n\n    test('should allow function assignments', (t) => {\n      ws.onopen = listen\n      t.assert.strictEqual(ws.onopen, listen)\n    })\n  })\n\n  describe('onerror', () => {\n    test('should be null initially', (t) => {\n      t.assert.strictEqual(ws.onerror, null)\n    })\n\n    test('should not allow non-function assignments', (t) => {\n      ws.onerror = 3\n      t.assert.strictEqual(ws.onerror, null)\n    })\n\n    test('should allow function assignments', (t) => {\n      ws.onerror = listen\n      t.assert.strictEqual(ws.onerror, listen)\n    })\n  })\n\n  describe('onclose', () => {\n    test('should be null initially', (t) => {\n      t.assert.strictEqual(ws.onclose, null)\n    })\n\n    test('should not allow non-function assignments', (t) => {\n      ws.onclose = 3\n      t.assert.strictEqual(ws.onclose, null)\n    })\n\n    test('should allow function assignments', (t) => {\n      ws.onclose = listen\n      t.assert.strictEqual(ws.onclose, listen)\n    })\n  })\n\n  describe('onmessage', () => {\n    test('should be null initially', (t) => {\n      t.assert.strictEqual(ws.onmessage, null)\n    })\n\n    test('should not allow non-function assignments', (t) => {\n      ws.onmessage = 3\n      t.assert.strictEqual(ws.onmessage, null)\n    })\n\n    test('should allow function assignments', (t) => {\n      ws.onmessage = listen\n      t.assert.strictEqual(ws.onmessage, listen)\n    })\n  })\n})\n\ndescribe('CloseEvent WPTs ported', () => {\n  test('initCloseEvent', (t) => {\n    // Taken from websockets/interfaces/CloseEvent/historical.html\n    t.assert.ok(!('initCloseEvent' in CloseEvent.prototype))\n    t.assert.ok(!('initCloseEvent' in new CloseEvent('close')))\n  })\n\n  test('CloseEvent constructor', (t) => {\n    // Taken from websockets/interfaces/CloseEvent/constructor.html\n\n    {\n      const event = new CloseEvent('foo')\n\n      t.assert.ok(event instanceof CloseEvent, 'should be a CloseEvent')\n      t.assert.strictEqual(event.type, 'foo')\n      t.assert.ok(!event.bubbles, 'bubbles')\n      t.assert.ok(!event.cancelable, 'cancelable')\n      t.assert.ok(!event.wasClean, 'wasClean')\n      t.assert.strictEqual(event.code, 0)\n      t.assert.strictEqual(event.reason, '')\n    }\n\n    {\n      const event = new CloseEvent('foo', {\n        bubbles: true,\n        cancelable: true,\n        wasClean: true,\n        code: 7,\n        reason: 'x'\n      })\n      t.assert.ok(event instanceof CloseEvent, 'should be a CloseEvent')\n      t.assert.strictEqual(event.type, 'foo')\n      t.assert.ok(event.bubbles, 'bubbles')\n      t.assert.ok(event.cancelable, 'cancelable')\n      t.assert.ok(event.wasClean, 'wasClean')\n      t.assert.strictEqual(event.code, 7)\n      t.assert.strictEqual(event.reason, 'x')\n    }\n  })\n})\n\ndescribe('ErrorEvent WPTs ported', () => {\n  test('Synthetic ErrorEvent', (t) => {\n    // Taken from html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html\n\n    {\n      const e = new ErrorEvent('error')\n      t.assert.strictEqual(e.message, '')\n      t.assert.strictEqual(e.filename, '')\n      t.assert.strictEqual(e.lineno, 0)\n      t.assert.strictEqual(e.colno, 0)\n      t.assert.strictEqual(e.error, undefined)\n    }\n\n    {\n      const e = new ErrorEvent('error', { error: null })\n      t.assert.strictEqual(e.error, null)\n    }\n\n    {\n      const e = new ErrorEvent('error', { error: undefined })\n      t.assert.strictEqual(e.error, undefined)\n    }\n\n    {\n      const e = new ErrorEvent('error', { error: 'foo' })\n      t.assert.strictEqual(e.error, 'foo')\n    }\n  })\n\n  test('webidl', (t) => {\n    // Taken from webidl/ecmascript-binding/no-regexp-special-casing.any.js\n\n    const regExp = new RegExp()\n    regExp.message = 'some message'\n\n    const errorEvent = new ErrorEvent('type', regExp)\n\n    t.assert.strictEqual(errorEvent.message, 'some message')\n  })\n\n  test('initErrorEvent', (t) => {\n    // Taken from workers/Worker_dispatchEvent_ErrorEvent.htm\n\n    const e = new ErrorEvent('error')\n    t.assert.ok(!('initErrorEvent' in e), 'should not be supported')\n  })\n})\n"
  },
  {
    "path": "test/websocket/fragments.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst diagnosticsChannel = require('node:diagnostics_channel')\n\ntest('Fragmented frame with a ping frame in the middle of it', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    socket.write(Buffer.from([0x01, 0x03, 0x48, 0x65, 0x6c])) // Text frame \"Hel\"\n    socket.write(Buffer.from([0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])) // ping \"Hello\"\n    socket.write(Buffer.from([0x80, 0x02, 0x6c, 0x6f])) // Text frame \"lo\"\n  })\n\n  after(() => {\n    for (const client of server.clients) {\n      client.close()\n    }\n\n    server.close()\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  diagnosticsChannel.channel('undici:websocket:ping').subscribe(\n    ({ payload }) => t.assert.deepStrictEqual(payload, Buffer.from('Hello'))\n  )\n\n  return new Promise((resolve) => {\n    ws.addEventListener('message', ({ data }) => {\n      t.assert.strictEqual(data, 'Hello')\n\n      ws.close()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/frame.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebsocketFrameSend } = require('../../lib/web/websocket/frame')\nconst { opcodes } = require('../../lib/web/websocket/constants')\n\n// Always be above all tests.\ntest('Don not use pooled buffer in mask pool', (t) => {\n  const allocUnsafe = Buffer.allocUnsafe\n  let counter = 0\n  try {\n    Buffer.allocUnsafe = (n) => {\n      counter++\n      return allocUnsafe(n)\n    }\n    // create mask pool\n    new WebsocketFrameSend(Buffer.alloc(0)).createFrame(opcodes.BINARY)\n    t.assert.strictEqual(counter, 1)\n  } finally {\n    Buffer.allocUnsafe = allocUnsafe\n  }\n})\n\ntest('Writing 16-bit frame length value at correct offset when buffer has a non-zero byteOffset', (t) => {\n  /*\n  When writing 16-bit frame lengths, a `DataView` was being used without setting a `byteOffset` into the buffer:\n  i.e. `new DataView(buffer.buffer)` instead of `new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)`.\n  Small `Buffers` returned by `allocUnsafe` are usually returned from the buffer pool, and thus have a non-zero `byteOffset`.\n  Invalid frames were therefore being returned in that case.\n  */\n  const payloadLength = 126 // 126 bytes is the smallest payload to trigger a 16-bit length field\n  const smallBuffer = Buffer.allocUnsafe(1) // make it very likely that the next buffer returned by allocUnsafe DOESN'T have a zero byteOffset\n  const payload = Buffer.allocUnsafe(payloadLength).fill(0)\n  const frame = new WebsocketFrameSend(payload).createFrame(opcodes.BINARY)\n\n  t.assert.strictEqual(frame[2], payloadLength >>> 8)\n  t.assert.strictEqual(frame[3], payloadLength & 0xff)\n  t.assert.strictEqual(smallBuffer.length, 1) // ensure smallBuffer can't be garbage-collected too soon\n})\n"
  },
  {
    "path": "test/websocket/issue-2679.js",
    "content": "'use strict'\n\nconst { test, after } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Close without receiving code does not send an invalid payload', async (t) => {\n  const server = new WebSocketServer({ port: 0 })\n  after(() => {\n    server.close()\n  })\n\n  await once(server, 'listening')\n\n  server.on('connection', (sock, request) => {\n    sock.close()\n  })\n\n  server.on('error', (err) => t.assert.ifError(err))\n\n  const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n  await once(client, 'open')\n\n  await once(client, 'close')\n})\n"
  },
  {
    "path": "test/websocket/issue-2844.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('The server must reply with at least one subprotocol the client sends', (t, done) => {\n  t.plan(2)\n\n  const wss = new WebSocketServer({\n    handleProtocols: (protocols) => {\n      t.assert.deepStrictEqual(protocols, new Set(['msgpack', 'json']))\n\n      return protocols.values().next().value\n    },\n    port: 0\n  })\n  t.after(() => {\n    wss.close()\n  })\n\n  wss.on('connection', (ws) => {\n    ws.on('error', t.assert.fail)\n    ws.send('something')\n  })\n\n  once(wss, 'listening').then(() => {\n    const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {\n      protocols: ['msgpack', 'json']\n    })\n\n    ws.onerror = t.assert.fail\n    ws.onopen = () => {\n      t.assert.deepStrictEqual(ws.protocol, 'msgpack')\n      done()\n    }\n\n    t.after(() => {\n      ws.close()\n    })\n  })\n})\n\ntest('The connection fails when the client sends subprotocols that the server does not responc with', async (t) => {\n  const wss = new WebSocketServer({\n    handleProtocols: () => false,\n    port: 0\n  })\n\n  wss.on('connection', (ws) => {\n    ws.on('error', t.assert.fail)\n    ws.send('something')\n  })\n\n  await once(wss, 'listening')\n\n  const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {\n    protocols: ['json']\n  })\n\n  ws.onerror = t.assert.ok.bind(null, true)\n  // The server will try to send 'something', this ensures that the connection\n  // fails during the handshake and doesn't receive any messages.\n  ws.onmessage = t.assert.fail\n\n  t.after(() => {\n    wss.close()\n    ws.close()\n  })\n})\n"
  },
  {
    "path": "test/websocket/issue-2859.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst diagnosticsChannel = require('node:diagnostics_channel')\n\ntest('Fragmented frame with a ping frame in the first of it', (t, done) => {\n  t.plan(2)\n\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    socket.write(Buffer.from([0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])) // ping \"Hello\"\n    socket.write(Buffer.from([0x01, 0x03, 0x48, 0x65, 0x6c])) // Text frame \"Hel\"\n    socket.write(Buffer.from([0x80, 0x02, 0x6c, 0x6f])) // Text frame \"lo\"\n  })\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n\n  const ws = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n  diagnosticsChannel.channel('undici:websocket:ping').subscribe(\n    ({ payload }) => t.assert.deepStrictEqual(payload, Buffer.from('Hello'))\n  )\n\n  ws.addEventListener('message', ({ data }) => {\n    t.assert.strictEqual(data, 'Hello')\n    done()\n  })\n})\n"
  },
  {
    "path": "test/websocket/issue-3202.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Receiving frame with payload length 0 works', (t, done) => {\n  t.plan(1)\n\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (socket) => {\n    socket.on('message', () => {\n      socket.send('')\n    })\n  })\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n\n  const ws = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.send('Hi')\n  })\n\n  ws.addEventListener('message', () => {\n    t.assert.ok(true)\n    done()\n  })\n})\n"
  },
  {
    "path": "test/websocket/issue-3506.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\n\ntest('readyState is set on fail', (t, done) => {\n  t.plan(1)\n  const ws = new WebSocket('ws://localhost:1')\n\n  t.after(() => ws.close())\n\n  ws.addEventListener('error', () => {\n    t.assert.deepStrictEqual(ws.readyState, WebSocket.CLOSED)\n    done()\n  })\n})\n"
  },
  {
    "path": "test/websocket/issue-3546.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('first error than close event is fired on failed connection', async (t) => {\n  t.plan(4)\n  const ws = new WebSocket('ws://localhost:1')\n\n  let orderOfEvents = 0\n\n  ws.addEventListener('error', () => {\n    t.assert.strictEqual(orderOfEvents++, 0)\n    t.assert.strictEqual(ws.readyState, WebSocket.CLOSED)\n  })\n\n  ws.addEventListener('close', () => {\n    t.assert.strictEqual(orderOfEvents++, 1)\n    t.assert.strictEqual(ws.readyState, WebSocket.CLOSED)\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/issue-3697-2399493917.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\n// https://github.com/nodejs/undici/issues/3697#issuecomment-2399493917\ntest('closing before a connection is established changes readyState', async (t) => {\n  t.plan(1)\n\n  const ws = new WebSocket('wss://localhost')\n  ws.onclose = () => {\n    t.assert.strictEqual(ws.readyState, WebSocket.CLOSED)\n  }\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/issue-4273.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('first error than close event is fired on failed connection', async (t) => {\n  t.plan(1)\n\n  const ws = new WebSocket('ws://localhost:1')\n\n  ws.addEventListener('error', (ev) => {\n    t.assert.ok(ev.error instanceof TypeError)\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/issue-4487.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\n\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('should be emit error event on aborted connection', async (t) => {\n  const wss = new WebSocketServer({ port: 0 })\n\n  t.after(() => wss.close())\n\n  wss.on('connection', (ws) => ws.terminate())\n\n  await once(wss, 'listening')\n\n  const ws = new WebSocket(`http://localhost:${wss.address().port}`)\n\n  let errorEmitted = false\n  // eslint-disable-next-line no-return-assign\n  ws.onerror = () => errorEmitted = true\n  await once(ws, 'close')\n\n  t.assert.ok(errorEmitted)\n})\n"
  },
  {
    "path": "test/websocket/issue-4628.js",
    "content": "'use strict'\n\nconst assert = require('node:assert')\nconst { test } = require('node:test')\nconst { WebSocket } = require('../..')\n\ntest('closing before connection is established should only fire error and close events once', (t) => {\n  t.plan(2)\n\n  t.after(() => assert.deepStrictEqual(events, ['error', 'close']))\n\n  const events = []\n  const ws = new WebSocket('wss://example.com/')\n\n  ws.onopen = t.assert.fail\n\n  ws.addEventListener('error', () => {\n    t.assert.ok(true, 'error event fired')\n    events.push('error')\n  })\n\n  ws.addEventListener('close', () => {\n    t.assert.ok(true, 'close event fired')\n    events.push('close')\n  })\n\n  ws.close()\n})\n"
  },
  {
    "path": "test/websocket/issue-4889.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { WebSocketServer } = require('ws')\n\nconst { WebSocket } = require('../..')\nconst { closeServerAsPromise } = require('../utils/node-http')\n\n// https://github.com/nodejs/undici/issues/4889\ntest('websocket auth retry preserves path when URL contains credentials', async (t) => {\n  const expectedAuth = `Basic ${Buffer.from('user:pass').toString('base64')}`\n\n  let attempts = 0\n\n  const server = createServer()\n  const wss = new WebSocketServer({ noServer: true })\n\n  t.after(async () => {\n    for (const client of wss.clients) {\n      client.terminate()\n    }\n\n    await new Promise((resolve) => wss.close(resolve))\n    await closeServerAsPromise(server)()\n  })\n\n  server.on('upgrade', (req, socket, head) => {\n    attempts++\n    t.assert.strictEqual(req.url, '/path')\n\n    if (attempts === 1) {\n      t.assert.strictEqual(req.headers.authorization, undefined)\n      socket.write(\n        'HTTP/1.1 401 Unauthorized\\r\\n' +\n        'WWW-Authenticate: Basic realm=\"test\"\\r\\n' +\n        'Content-Length: 0\\r\\n' +\n        '\\r\\n'\n      )\n      socket.destroy()\n      return\n    }\n\n    if (attempts === 2) {\n      t.assert.strictEqual(req.headers.authorization, expectedAuth)\n      wss.handleUpgrade(req, socket, head, (websocket) => {\n        wss.emit('connection', websocket, req)\n      })\n      return\n    }\n\n    t.assert.fail(`unexpected upgrade attempt #${attempts}`)\n  })\n\n  server.listen(0, '127.0.0.1')\n  await once(server, 'listening')\n\n  const ws = new WebSocket(`ws://user:pass@127.0.0.1:${server.address().port}/path`)\n\n  await new Promise((resolve, reject) => {\n    ws.addEventListener('open', resolve, { once: true })\n    ws.addEventListener('error', ({ error }) => reject(error), { once: true })\n  })\n\n  t.assert.strictEqual(attempts, 2)\n})\n"
  },
  {
    "path": "test/websocket/messageevent.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { MessageEvent } = require('../..')\n\ntest('test/parallel/test-messageevent-brandcheck.js', (t) => {\n  [\n    'data',\n    'origin',\n    'lastEventId',\n    'source',\n    'ports'\n  ].forEach((i) => {\n    t.assert.throws(() => Reflect.get(MessageEvent.prototype, i, {}), {\n      constructor: TypeError,\n      message: 'Illegal invocation'\n    })\n  })\n})\n\ntest('test/parallel/test-worker-message-port.js', (t) => {\n  const dummyPort = new MessageChannel().port1\n\n  for (const [args, expected] of [\n    [\n      ['message'],\n      {\n        type: 'message',\n        data: null,\n        origin: '',\n        lastEventId: '',\n        source: null,\n        ports: []\n      }\n    ],\n    [\n      ['message', { data: undefined, origin: 'foo' }],\n      {\n        type: 'message',\n        data: null,\n        origin: 'foo',\n        lastEventId: '',\n        source: null,\n        ports: []\n      }\n    ],\n    [\n      ['message', { data: 2, origin: 1, lastEventId: 0 }],\n      {\n        type: 'message',\n        data: 2,\n        origin: '1',\n        lastEventId: '0',\n        source: null,\n        ports: []\n      }\n    ],\n    [\n      ['message', { lastEventId: 'foo' }],\n      {\n        type: 'message',\n        data: null,\n        origin: '',\n        lastEventId: 'foo',\n        source: null,\n        ports: []\n      }\n    ],\n    [\n      ['messageerror', { lastEventId: 'foo', source: dummyPort }],\n      {\n        type: 'messageerror',\n        data: null,\n        origin: '',\n        lastEventId: 'foo',\n        source: dummyPort,\n        ports: []\n      }\n    ],\n    [\n      ['message', { ports: [dummyPort], source: null }],\n      {\n        type: 'message',\n        data: null,\n        origin: '',\n        lastEventId: '',\n        source: null,\n        ports: [dummyPort]\n      }\n    ]\n  ]) {\n    const ev = new MessageEvent(...args)\n    const { type, data, origin, lastEventId, source, ports } = ev\n    t.assert.deepStrictEqual(expected, {\n      type, data, origin, lastEventId, source, ports\n    })\n  }\n\n  t.assert.throws(() => new MessageEvent('message', { source: 1 }), {\n    constructor: TypeError,\n    message: 'MessageEvent constructor: Expected eventInitDict.source (\"1\") to be an instance of MessagePort.'\n  })\n  t.assert.throws(() => new MessageEvent('message', { source: {} }), {\n    constructor: TypeError,\n    message: 'MessageEvent constructor: Expected eventInitDict.source (\"{}\") to be an instance of MessagePort.'\n  })\n  t.assert.throws(() => new MessageEvent('message', { ports: 0 }), {\n    constructor: TypeError,\n    message: 'MessageEvent constructor: eventInitDict.ports (0) is not iterable.'\n  })\n  t.assert.throws(() => new MessageEvent('message', { ports: [null] }), {\n    constructor: TypeError,\n    message: 'MessageEvent constructor: Expected eventInitDict.ports[0] (\"null\") to be an instance of MessagePort.'\n  })\n  t.assert.throws(() =>\n    new MessageEvent('message', { ports: [{}] })\n  , {\n    constructor: TypeError,\n    message: 'MessageEvent constructor: Expected eventInitDict.ports[0] (\"{}\") to be an instance of MessagePort.'\n  })\n\n  t.assert.ok(new MessageEvent('message') instanceof Event)\n\n  // https://github.com/nodejs/node/issues/51767\n  const event = new MessageEvent('type', { cancelable: true })\n  event.preventDefault()\n\n  t.assert.ok(event.cancelable)\n  t.assert.ok(event.defaultPrevented)\n})\n\ntest('bug in node core', (t) => {\n  // In node core, this will throw an error.\n  new MessageEvent('', null) // eslint-disable-line no-new\n})\n"
  },
  {
    "path": "test/websocket/opening-handshake.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { createServer } = require('node:http')\nconst { createSecureServer } = require('node:http2')\n\nconst { tspl } = require('@matteo.collina/tspl')\nconst { WebSocketServer, WebSocket: WSWebsocket } = require('ws')\nconst { key, cert } = require('@metcoder95/https-pem')\nconst { WebSocket, Agent } = require('../..')\nconst { runtimeFeatures } = require('../../lib/util/runtime-features')\nconst { uid } = require('../../lib/web/websocket/constants')\n\nconst crypto = runtimeFeatures.has('crypto')\n  ? require('node:crypto')\n  : null\n\ntest('WebSocket connecting to server that isn\\'t a Websocket server', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      t.assert.strictEqual(req.headers.connection, 'upgrade')\n      t.assert.strictEqual(req.headers.upgrade, 'websocket')\n      t.assert.ok(req.headers['sec-websocket-key'])\n      t.assert.strictEqual(req.headers['sec-websocket-version'], '13')\n\n      res.end()\n      server.unref()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n      // Server isn't a websocket server\n      ws.onmessage = ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Open event is emitted', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    server.on('connection', (ws) => {\n      ws.close(1000)\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    ws.onmessage = ws.onerror = reject\n    ws.addEventListener('open', (t) => {\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('WebSocket on H2', { skip: crypto == null }, async (t) => {\n  const planner = tspl(t, { plan: 6 })\n  const server = createSecureServer({ cert, key, allowHTTP1: true, settings: { enableConnectProtocol: true } })\n  const wsServer = new WebSocketServer({ noServer: true })\n  server.on('stream', (stream, headers) => {\n    planner.equal(headers[':method'], 'CONNECT')\n    planner.equal(headers[':protocol'], 'websocket')\n    planner.equal(headers[':path'], '/')\n    planner.equal(headers[':scheme'], 'https')\n\n    stream.respond({\n      ':status': 200,\n      'sec-websocket-protocol': headers['sec-websocket-protocol'],\n      'sec-websocket-accept': crypto.hash('sha1', `${headers['sec-websocket-key']}${uid}`, 'base64')\n    })\n    const ws = new WSWebsocket(null, null, { autoPong: true })\n    ws.setSocket(stream, Buffer.alloc(0), {\n      maxPayload: 104857600,\n      skipUTF8Validation: false\n    })\n\n    wsServer.emit('connection', ws, stream)\n  })\n\n  wsServer.on('connection', (ws) => {\n    ws.send('hello')\n  })\n\n  server.listen(0)\n  await once(server, 'listening')\n\n  const dispatcher = new Agent({\n    allowH2: true,\n    connect: {\n      rejectUnauthorized: false\n    }\n  })\n  const ws = new WebSocket(`wss://localhost:${server.address().port}`, { dispatcher, protocols: ['chat'] })\n\n  t.after(() => {\n    // Cleanup - Seems that due to the nature of H2 we need to remove the error listener\n    // TODO: investigate if this is a bug\n    ws.onerror = null\n    return new Promise((resolve) => {\n      ws.close()\n      server.close()\n      wsServer.close(() => {\n        dispatcher.close().then(resolve)\n      })\n    })\n  })\n\n  ws.onmessage = (evt) => planner.equal(evt.data, 'hello')\n  ws.onerror = (err) => {\n    planner.fail(err)\n  }\n  ws.addEventListener('open', () => planner.ok(true))\n\n  await planner.completed\n})\n\ntest('WebSocket connecting to server that isn\\'t a Websocket server (h2 - supports extended CONNECT protocol)', async (t) => {\n  const planner = tspl(t, { plan: 6 })\n  const h2Server = createSecureServer({ cert, key, settings: { enableConnectProtocol: true } })\n    .on('stream', (stream, headers) => {\n      planner.equal(headers[':method'], 'CONNECT')\n      planner.equal(headers[':protocol'], 'websocket')\n      planner.ok(headers['sec-websocket-key'])\n      planner.equal(headers['sec-websocket-protocol'], 'chat')\n      planner.equal(headers['sec-websocket-version'], '13')\n\n      stream.respond({ ':status': 200 })\n      stream.close(8) // NGHTTP2_CANCEL\n    })\n    .listen(0)\n\n  await once(h2Server, 'listening')\n\n  const dispatcher = new Agent({\n    allowH2: true,\n    connect: {\n      rejectUnauthorized: false\n    }\n  })\n  const ws = new WebSocket(`wss://localhost:${h2Server.address().port}`, { dispatcher, protocols: ['chat'] })\n  const cleaner = setupListener()\n  ws.onmessage = ws.onopen = () => planner.fail('should not open')\n\n  t.after(() => {\n    cleaner()\n    dispatcher.close()\n    ws.close()\n    h2Server.close()\n  })\n\n  await planner.completed\n\n  function setupListener () {\n    ws.addEventListener('error', listener)\n\n    return () => { ws.removeEventListener('error', listener) }\n\n    function listener ({ error }) {\n      planner.ok(error)\n    }\n  }\n})\n\ntest('WebSocket on H2 with a server that does not support extended CONNECT protocol', async (t) => {\n  const planner = tspl(t, { plan: 1 })\n  const h2Server = createSecureServer({ cert, key, settings: { enableConnectProtocol: false } })\n    .on('stream', (stream) => {\n      stream.respond({ ':status': 200 })\n      stream.end('')\n      h2Server.unref()\n    })\n    .listen(0)\n\n  await once(h2Server, 'listening')\n  t.after(() => { h2Server.close() })\n\n  const dispatcher = new Agent({\n    allowH2: true,\n    connect: {\n      rejectUnauthorized: false\n    }\n  })\n  const ws = new WebSocket(`wss://localhost:${h2Server.address().port}`, { dispatcher, protocols: ['chat'] })\n\n  t.after(() => { return ws.close() || dispatcher.close() })\n\n  ws.onmessage = ws.onopen = () => planner.fail('should not open')\n  ws.addEventListener('error', ({ error }) => {\n    planner.ok(error)\n    ws.close()\n    h2Server.close()\n  })\n\n  await planner.completed\n})\n\ntest('Multiple protocols are joined by a comma', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    server.on('connection', (ws, req) => {\n      t.assert.strictEqual(req.headers['sec-websocket-protocol'], 'chat, echo')\n\n      ws.close(1000)\n      server.close()\n      resolve()\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`, ['chat', 'echo'])\n    ws.addEventListener('open', () => ws.close())\n  })\n})\n\ntest('Server doesn\\'t send Sec-WebSocket-Protocol header when protocols are used', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.statusCode = 101\n\n      req.socket.destroy()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`, 'chat')\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Server sends invalid Upgrade header', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('Upgrade', 'NotWebSocket')\n      res.statusCode = 101\n\n      req.socket.destroy()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Server sends invalid Connection header', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('Upgrade', 'websocket')\n      res.setHeader('Connection', 'downgrade')\n      res.statusCode = 101\n\n      req.socket.destroy()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Server sends invalid Sec-WebSocket-Accept header', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      res.setHeader('Upgrade', 'websocket')\n      res.setHeader('Connection', 'upgrade')\n      res.setHeader('Sec-WebSocket-Accept', 'abc')\n      res.statusCode = 101\n\n      req.socket.destroy()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Server sends invalid Sec-WebSocket-Extensions header', { skip: runtimeFeatures.has('crypto') === false }, (t) => {\n  return new Promise((resolve, reject) => {\n    const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const key = req.headers['sec-websocket-key']\n      t.assert.ok(key)\n\n      const accept = require('node:crypto').hash('sha1', key + uid, 'base64')\n\n      res.setHeader('Upgrade', 'websocket')\n      res.setHeader('Connection', 'upgrade')\n      res.setHeader('Sec-WebSocket-Accept', accept)\n      res.setHeader('Sec-WebSocket-Extensions', 'InvalidExtension')\n      res.statusCode = 101\n\n      res.end()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n\ntest('Server sends invalid Sec-WebSocket-Extensions header', { skip: runtimeFeatures.has('crypto') === false }, (t) => {\n  const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n\n  return new Promise((resolve, reject) => {\n    const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {\n      const key = req.headers['sec-websocket-key']\n      t.assert.ok(key)\n\n      const accept = require('node:crypto').hash('sha1', key + uid, 'base64')\n\n      res.setHeader('Upgrade', 'websocket')\n      res.setHeader('Connection', 'upgrade')\n      res.setHeader('Sec-WebSocket-Accept', accept)\n      res.setHeader('Sec-WebSocket-Protocol', 'echo') // <--\n      res.statusCode = 101\n\n      res.end()\n    }).listen(0, () => {\n      const ws = new WebSocket(`ws://localhost:${server.address().port}`, 'chat')\n\n      ws.onopen = reject\n\n      ws.addEventListener('error', ({ error }) => {\n        t.assert.ok(error)\n        server.close()\n        resolve()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/permessage-deflate-limit.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { once } = require('node:events')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Compressed message under limit decompresses successfully', async (t) => {\n  const server = new WebSocketServer({\n    port: 0,\n    perMessageDeflate: true\n  })\n\n  t.after(() => server.close())\n\n  await once(server, 'listening')\n\n  server.on('connection', (ws) => {\n    // Send 1 KB of data (well under any reasonable limit)\n    ws.send(Buffer.alloc(1024, 0x41), { binary: true })\n  })\n\n  const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n  const [event] = await once(client, 'message')\n  t.assert.strictEqual(event.data.size, 1024)\n  client.close()\n})\n"
  },
  {
    "path": "test/websocket/permessage-deflate-windowbits.js",
    "content": "'use strict'\n\nconst { describe, test, after } = require('node:test')\nconst { once } = require('node:events')\nconst http = require('node:http')\nconst crypto = require('node:crypto')\nconst zlib = require('node:zlib')\nconst { WebSocket } = require('../..')\nconst { isValidClientWindowBits } = require('../../lib/web/websocket/util')\n\n/**\n * Creates a minimal WebSocket server that responds with a custom\n * server_max_window_bits value and sends a compressed frame.\n */\nfunction createMaliciousServer (windowBitsValue) {\n  const server = http.createServer()\n  const sockets = new Set()\n\n  server.on('upgrade', (req, socket) => {\n    sockets.add(socket)\n    socket.on('close', () => sockets.delete(socket))\n\n    const key = req.headers['sec-websocket-key']\n    const accept = crypto\n      .createHash('sha1')\n      .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')\n      .digest('base64')\n\n    const headers = [\n      'HTTP/1.1 101 Switching Protocols',\n      'Upgrade: websocket',\n      'Connection: Upgrade',\n      `Sec-WebSocket-Accept: ${accept}`,\n      `Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=${windowBitsValue}`,\n      '', ''\n    ]\n\n    socket.write(headers.join('\\r\\n'))\n\n    // Send a compressed frame to trigger decompression\n    setTimeout(() => {\n      if (!socket.destroyed) {\n        const payload = zlib.deflateRawSync(Buffer.from('Hello'))\n        // Remove trailing 00 00 ff ff if present\n        const trimmed = payload.subarray(0, payload.length - 4)\n        const frame = makeWsFrame({ opcode: 2, rsv1: true, payload: trimmed })\n        socket.write(frame)\n      }\n    }, 50)\n  })\n\n  // Override close to also destroy sockets\n  const originalClose = server.close.bind(server)\n  server.close = (cb) => {\n    for (const socket of sockets) {\n      socket.destroy()\n    }\n    sockets.clear()\n    originalClose(cb)\n  }\n\n  return server\n}\n\n/**\n * Creates a WebSocket frame (server-to-client, unmasked)\n */\nfunction makeWsFrame ({ opcode, rsv1, payload }) {\n  const fin = 1\n  const b0 = (fin << 7) | ((rsv1 ? 1 : 0) << 6) | (opcode & 0x0f)\n  const len = payload.length\n\n  let header\n  if (len <= 125) {\n    header = Buffer.from([b0, len])\n  } else if (len <= 0xffff) {\n    header = Buffer.alloc(4)\n    header[0] = b0\n    header[1] = 126\n    header.writeUInt16BE(len, 2)\n  } else {\n    header = Buffer.alloc(10)\n    header[0] = b0\n    header[1] = 127\n    header.writeUInt32BE(0, 2)\n    header.writeUInt32BE(len, 6)\n  }\n\n  return Buffer.concat([header, payload])\n}\n\ndescribe('isValidClientWindowBits', () => {\n  test('rejects empty string', (t) => {\n    t.assert.strictEqual(isValidClientWindowBits(''), false)\n  })\n\n  test('rejects values below 8', (t) => {\n    t.assert.strictEqual(isValidClientWindowBits('0'), false)\n    t.assert.strictEqual(isValidClientWindowBits('1'), false)\n    t.assert.strictEqual(isValidClientWindowBits('7'), false)\n  })\n\n  test('accepts values 8-15', (t) => {\n    for (let i = 8; i <= 15; i++) {\n      t.assert.strictEqual(isValidClientWindowBits(String(i)), true, `${i} should be valid`)\n    }\n  })\n\n  test('rejects values above 15', (t) => {\n    t.assert.strictEqual(isValidClientWindowBits('16'), false)\n    t.assert.strictEqual(isValidClientWindowBits('100'), false)\n    t.assert.strictEqual(isValidClientWindowBits('1000'), false)\n    t.assert.strictEqual(isValidClientWindowBits('999999'), false)\n  })\n\n  test('rejects non-numeric values', (t) => {\n    t.assert.strictEqual(isValidClientWindowBits('abc'), false)\n    t.assert.strictEqual(isValidClientWindowBits('12a'), false)\n    t.assert.strictEqual(isValidClientWindowBits('-1'), false)\n    t.assert.strictEqual(isValidClientWindowBits('8.5'), false)\n  })\n})\n\ndescribe('permessage-deflate server_max_window_bits', () => {\n  test('server_max_window_bits=8 works correctly', async (t) => {\n    const server = createMaliciousServer('8')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'message')\n    t.assert.ok(event.data)\n    client.close()\n  })\n\n  test('server_max_window_bits=15 works correctly', async (t) => {\n    const server = createMaliciousServer('15')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'message')\n    t.assert.ok(event.data)\n    client.close()\n  })\n\n  test('server_max_window_bits=0 is rejected gracefully', async (t) => {\n    const server = createMaliciousServer('0')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'error')\n    t.assert.ok(event.error instanceof Error)\n  })\n\n  test('server_max_window_bits=7 is rejected gracefully', async (t) => {\n    const server = createMaliciousServer('7')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'error')\n    t.assert.ok(event.error instanceof Error)\n  })\n\n  test('server_max_window_bits=16 is rejected gracefully', async (t) => {\n    const server = createMaliciousServer('16')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'error')\n    t.assert.ok(event.error instanceof Error)\n  })\n\n  test('server_max_window_bits=1000 is rejected gracefully (PoC attack)', async (t) => {\n    const server = createMaliciousServer('1000')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    const [event] = await once(client, 'error')\n    // The key assertion: we got an error event instead of crashing\n    t.assert.ok(event.error instanceof Error, 'Should receive an Error')\n  })\n\n  test('no uncaught exception with invalid windowBits', async (t) => {\n    let uncaughtException = false\n\n    const handler = () => {\n      uncaughtException = true\n    }\n    process.on('uncaughtException', handler)\n\n    const server = createMaliciousServer('1000')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => {\n      server.close()\n      process.off('uncaughtException', handler)\n    })\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    await Promise.race([\n      once(client, 'error'),\n      once(client, 'close'),\n      new Promise(resolve => setTimeout(resolve, 1000))\n    ])\n\n    t.assert.strictEqual(uncaughtException, false, 'No uncaught exception should occur')\n  })\n\n  test('invalid windowBits closes connection without crashing process', async (t) => {\n    const server = createMaliciousServer('999999')\n    await new Promise(resolve => server.listen(0, resolve))\n    after(() => server.close())\n\n    const client = new WebSocket(`ws://127.0.0.1:${server.address().port}`)\n\n    // Wait for either error or close event\n    const result = await Promise.race([\n      once(client, 'error').then(([e]) => ({ type: 'error', event: e })),\n      once(client, 'close').then(([e]) => ({ type: 'close', event: e }))\n    ])\n\n    // Either error or close is acceptable, as long as we didn't crash\n    t.assert.ok(\n      result.type === 'error' || result.type === 'close',\n      'Connection should close gracefully'\n    )\n\n    // This assertion is reached = process did not crash\n    t.assert.ok(true, 'Process did not crash')\n  })\n})\n"
  },
  {
    "path": "test/websocket/ping-pong.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst diagnosticsChannel = require('node:diagnostics_channel')\nconst { WebSocket } = require('../..')\n\ntest('Receives ping and parses body', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    server.on('connection', (ws) => {\n      ws.ping('Hello, world')\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n    ws.onerror = ws.onmessage = reject\n\n    diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload }) => {\n      t.assert.deepStrictEqual(payload, Buffer.from('Hello, world'))\n      ws.close()\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('Receives pong and parses body', (t) => {\n  return new Promise((resolve, reject) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    server.on('connection', (ws) => {\n      ws.pong('Pong')\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n    ws.onerror = ws.onmessage = reject\n\n    diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {\n      t.assert.deepStrictEqual(payload, Buffer.from('Pong'))\n      server.close()\n      ws.close()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/ping-util.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket, ping } = require('../..')\nconst { once } = require('node:events')\n\ntest('ping', async (t) => {\n  t.plan(1)\n\n  const pingBody = Buffer.from('ping body')\n  const wss = new WebSocketServer({ port: 0 })\n\n  wss.on('connection', (ws) => {\n    ws.on('ping', (b) => {\n      t.assert.deepStrictEqual(b, pingBody)\n      ws.close()\n    })\n  })\n\n  const ws = new WebSocket(`ws://localhost:${wss.address().port}`)\n  ws.onopen = () => ping(ws, pingBody)\n\n  t.after(() => {\n    ws.close()\n    wss.close()\n  })\n\n  await once(ws, 'close')\n})\n\ntest('attempting to send invalid ping body', (t) => {\n  t.plan(2)\n\n  const wss = new WebSocketServer({ port: 0 })\n\n  wss.on('connection', (ws) => {\n    ws.on('ping', () => {\n      t.assert.fail('Received unexpected ping')\n    })\n  })\n\n  const ws = new WebSocket(`ws://localhost:${wss.address().port}`)\n\n  t.assert.throws(() => ping(ws, Buffer.from('a'.repeat(126))))\n  t.assert.throws(() => ping(ws, 'a'.repeat(125)))\n\n  t.after(() => {\n    ws.close()\n    wss.close()\n  })\n})\n\ntest('ping with no payload', async (t) => {\n  t.plan(1)\n\n  const wss = new WebSocketServer({ port: 0 })\n\n  wss.on('connection', (ws) => {\n    ws.on('ping', (b) => {\n      t.assert.deepStrictEqual(b, Buffer.alloc(0))\n      ws.close()\n    })\n  })\n\n  const ws = new WebSocket(`ws://localhost:${wss.address().port}`)\n  ws.onopen = () => ping(ws)\n\n  t.after(() => {\n    ws.close()\n    wss.close()\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/receive.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\ntest('Receiving a frame with a payload length > 2^31-1 bytes', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    socket.write(Buffer.from([0x81, 0x7F, 0xCA, 0xE5, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]))\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  return new Promise((resolve, reject) => {\n    ws.onmessage = reject\n\n    ws.addEventListener('error', (event) => {\n      t.assert.ok(event.error instanceof Error) // error event is emitted\n      ws.close()\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('Receiving a 64-bit payload length with a non-zero upper word', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    const socket = ws._socket\n\n    socket.write(Buffer.from([0x82, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]))\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  return new Promise((resolve, reject) => {\n    ws.onmessage = reject\n\n    ws.addEventListener('error', (event) => {\n      t.assert.ok(event.error instanceof Error)\n      ws.close()\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('Receiving an ArrayBuffer', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    ws.on('message', (data, isBinary) => {\n      ws.send(data, { binary: true })\n\n      ws.close(1000)\n    })\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.binaryType = 'what'\n    t.assert.strictEqual(ws.binaryType, 'blob')\n\n    ws.binaryType = 'arraybuffer' // <--\n    ws.send('Hello')\n  })\n\n  return new Promise((resolve) => {\n    ws.addEventListener('message', ({ data }) => {\n      t.assert.ok(data instanceof ArrayBuffer)\n      t.assert.deepStrictEqual(Buffer.from(data), Buffer.from('Hello'))\n      server.close()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/receiver-unit.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { ByteParser } = require('../../lib/web/websocket/receiver')\nconst { states } = require('../../lib/web/websocket/constants')\n\nconst invalidFrame = Buffer.from([0x82, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])\n\ntest('ByteParser rejects 64-bit payload lengths with a non-zero upper word', (t) => {\n  const calls = {\n    abort: 0,\n    close: 0\n  }\n\n  const handler = {\n    readyState: states.CONNECTING,\n    controller: {\n      abort: () => {\n        calls.abort += 1\n      }\n    },\n    onSocketClose: () => {\n      calls.close += 1\n    },\n    closeState: new Set()\n  }\n\n  const parser = new ByteParser(handler)\n\n  parser.write(invalidFrame)\n\n  return new Promise((resolve) => {\n    setImmediate(() => {\n      t.assert.strictEqual(calls.abort, 1)\n      t.assert.strictEqual(calls.close, 1)\n      parser.destroy()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/send-mutable.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\nconst { once } = require('node:events')\n\ntest('check cloned', async (t) => {\n  t.plan(2)\n\n  const server = new WebSocketServer({ port: 0 })\n  const buffer = new Uint8Array([0x61])\n\n  server.on('connection', (ws) => {\n    ws.on('message', (data) => {\n      t.assert.deepStrictEqual(data, Buffer.from([0x61]))\n      ws.close()\n    })\n  })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.send(new Blob([buffer]))\n    ws.send(buffer)\n    buffer[0] = 1\n  })\n\n  t.after(() => {\n    server.close()\n    ws.close()\n  })\n\n  await once(ws, 'close')\n})\n"
  },
  {
    "path": "test/websocket/send.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket } = require('../..')\n\n// the following three tests exercise different code paths because of the three\n// different ways a payload length may be specified in a WebSocket frame\n// (https://datatracker.ietf.org/doc/html/rfc6455#section-5.2)\n\ntest('Sending >= 2^16 bytes', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    ws.on('message', (m, isBinary) => {\n      ws.send(m, { binary: isBinary })\n    })\n  })\n\n  const payload = Buffer.allocUnsafe(2 ** 16).fill('Hello')\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.send(payload)\n  })\n\n  return new Promise((resolve) => {\n    ws.addEventListener('message', async ({ data }) => {\n      t.assert.ok(data instanceof Blob)\n      t.assert.strictEqual(data.size, payload.length)\n      t.assert.deepStrictEqual(Buffer.from(await data.arrayBuffer()), payload)\n\n      ws.close()\n      server.close()\n\n      resolve()\n    })\n  })\n})\n\ntest('Sending >= 126, < 2^16 bytes', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    ws.on('message', (m, isBinary) => {\n      ws.send(m, { binary: isBinary })\n    })\n  })\n\n  const payload = Buffer.allocUnsafe(126).fill('Hello')\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.send(payload)\n  })\n\n  return new Promise((resolve) => {\n    ws.addEventListener('message', async ({ data }) => {\n      t.assert.ok(data instanceof Blob)\n      t.assert.strictEqual(data.size, payload.length)\n      t.assert.deepStrictEqual(Buffer.from(await data.arrayBuffer()), payload)\n\n      ws.close()\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('Sending < 126 bytes', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  server.on('connection', (ws) => {\n    ws.on('message', (m, isBinary) => {\n      ws.send(m, { binary: isBinary })\n    })\n  })\n\n  const payload = Buffer.allocUnsafe(125).fill('Hello')\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  ws.addEventListener('open', () => {\n    ws.send(payload)\n  })\n\n  return new Promise((resolve) => {\n    ws.addEventListener('message', async ({ data }) => {\n      t.assert.ok(data instanceof Blob)\n      t.assert.strictEqual(data.size, payload.length)\n      t.assert.deepStrictEqual(Buffer.from(await data.arrayBuffer()), payload)\n\n      ws.close()\n      server.close()\n      resolve()\n    })\n  })\n})\n\ntest('Sending data after close', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  return new Promise((resolve, reject) => {\n    server.on('connection', (ws) => {\n      ws.on('message', reject)\n    })\n\n    ws.addEventListener('open', () => {\n      ws.close()\n      ws.send('Some message')\n      server.close()\n\n      resolve()\n    })\n\n    ws.addEventListener('error', reject)\n  })\n})\n\ntest('Sending data before connected', (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n  t.assert.throws(\n    () => ws.send('Not sent'),\n    {\n      name: 'InvalidStateError',\n      constructor: DOMException\n    }\n  )\n\n  t.assert.strictEqual(ws.readyState, WebSocket.CONNECTING)\n  server.close()\n})\n\ndescribe('Sending data to a server', () => {\n  test('Send with string', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    ws.addEventListener('open', () => {\n      ws.send('message')\n    })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('message', (data, isBinary) => {\n          t.assert.ok(!isBinary, 'Received text frame')\n          t.assert.deepStrictEqual(data, Buffer.from('message'))\n          ws.close(1000)\n          server.close()\n          resolve()\n        })\n      })\n    })\n  })\n\n  test('Send with ArrayBuffer', (t) => {\n    const message = new TextEncoder().encode('message')\n    const ab = new ArrayBuffer(7)\n    new Uint8Array(ab).set(message)\n\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    ws.addEventListener('open', () => {\n      ws.send(ab)\n    })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('message', (data, isBinary) => {\n          t.assert.ok(isBinary)\n          t.assert.deepStrictEqual(new Uint8Array(data), message)\n          ws.close(1000)\n          server.close()\n          resolve()\n        })\n      })\n    })\n  })\n\n  test('Send with Blob', (t) => {\n    const blob = new Blob(['hello'])\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    ws.addEventListener('open', () => {\n      ws.send(blob)\n    })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('message', (data, isBinary) => {\n          t.assert.ok(isBinary)\n          t.assert.deepStrictEqual(data, Buffer.from('hello'))\n          ws.close(1000)\n          server.close()\n          resolve()\n        })\n      })\n    })\n  })\n\n  test('Cannot send with SharedArrayBuffer', (t) => {\n    const sab = new SharedArrayBuffer(0)\n    const server = new WebSocketServer({ port: 0 })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`)\n\n    ws.addEventListener('open', () => {\n      ws.send(sab)\n    })\n\n    return new Promise((resolve) => {\n      server.on('connection', (ws) => {\n        ws.on('message', (data, isBinary) => {\n          t.assert.ok(!isBinary)\n          t.assert.deepStrictEqual(data, Buffer.from('[object SharedArrayBuffer]'))\n          ws.close(1000)\n          server.close()\n          resolve()\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/websocket/stream/readable-closed-after-close.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocketStream } = require('../../..')\n\n// https://github.com/ricea/websocketstream-explainer/issues/25\ntest('ReadableStream is closed properly', async (t) => {\n  const server = new WebSocketServer({ port: 0 })\n\n  const wss = new WebSocketStream(`ws://localhost:${server.address().port}`)\n\n  t.after(() => server.close())\n\n  const { readable, writable } = await wss.opened\n\n  const writer = writable.getWriter()\n  const reader = readable.getReader()\n\n  await writer.close()\n  await writer.closed\n\n  await Promise.allSettled([reader.closed, writer.closed])\n})\n"
  },
  {
    "path": "test/websocket/util.js",
    "content": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst { isValidSubprotocol } = require('../../lib/web/websocket/util')\n\ndescribe('isValidSubprotocol', () => {\n  test('empty string returns false', t => {\n    t.plan(1)\n    t.assert.strictEqual(isValidSubprotocol(''), false)\n  })\n\n  test('simple valid value returns false', t => {\n    t.plan(1)\n    t.assert.strictEqual(isValidSubprotocol('chat'), true)\n  })\n\n  test('empty string returns false', t => {\n    t.plan(1)\n    t.assert.strictEqual(isValidSubprotocol(''), false)\n  })\n\n  test('value with \"(),/:;<=>?@[\\\\]{} returns false', t => {\n    const chars = '\"(),/:;<=>?@[\\\\]{}'\n    t.plan(17)\n\n    for (let i = 0; i < chars.length; ++i) {\n      t.assert.strictEqual(isValidSubprotocol('valid' + chars[i]), false)\n    }\n  })\n})\n"
  },
  {
    "path": "test/websocket/websocketinit.js",
    "content": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst { WebSocketServer } = require('ws')\nconst { WebSocket, Dispatcher, Agent } = require('../..')\n\ndescribe('WebSocketInit', () => {\n  class WsDispatcher extends Dispatcher {\n    constructor () {\n      super()\n      this.agent = new Agent()\n    }\n\n    dispatch () {\n      return this.agent.dispatch(...arguments)\n    }\n  }\n\n  test('WebSocketInit as 2nd param', (t) => {\n    const server = new WebSocketServer({ port: 0 })\n\n    server.on('connection', (ws) => {\n      ws.send(Buffer.from('hello, world'))\n    })\n\n    const ws = new WebSocket(`ws://localhost:${server.address().port}`, {\n      dispatcher: new WsDispatcher()\n    })\n\n    return new Promise((resolve, reject) => {\n      ws.onerror = reject\n\n      ws.addEventListener('message', async (event) => {\n        t.assert.strictEqual(await event.data.text(), 'hello, world')\n        server.close()\n        ws.close()\n        resolve()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "types/README.md",
    "content": "# undici-types\n\nThis package is a dual-publish of the [undici](https://www.npmjs.com/package/undici) library types. The `undici` package **still contains types**. This package is for users who _only_ need undici types (such as for `@types/node`). It is published alongside every release of `undici`, so you can always use the same version.\n\n- [GitHub nodejs/undici](https://github.com/nodejs/undici)\n- [Undici Documentation](https://undici.nodejs.org/#/)\n"
  },
  {
    "path": "types/agent.d.ts",
    "content": "import { URL } from 'node:url'\nimport Pool from './pool'\nimport Dispatcher from './dispatcher'\nimport TClientStats from './client-stats'\nimport TPoolStats from './pool-stats'\n\nexport default Agent\n\ndeclare class Agent extends Dispatcher {\n  constructor (opts?: Agent.Options)\n  /** `true` after `dispatcher.close()` has been called. */\n  closed: boolean\n  /** `true` after `dispatcher.destroyed()` has been called or `dispatcher.close()` has been called and the dispatcher shutdown has completed. */\n  destroyed: boolean\n  /** Dispatches a request. */\n  dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean\n  /** Aggregate stats for a Agent by origin. */\n  readonly stats: Record<string, TClientStats | TPoolStats>\n}\n\ndeclare namespace Agent {\n  export interface Options extends Pool.Options {\n    /** Default: `(origin, opts) => new Pool(origin, opts)`. */\n    factory?(origin: string | URL, opts: Object): Dispatcher;\n\n    interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options['interceptors']\n    maxOrigins?: number\n  }\n\n  export interface DispatchOptions extends Dispatcher.DispatchOptions {\n  }\n}\n"
  },
  {
    "path": "types/api.d.ts",
    "content": "import { URL, UrlObject } from 'node:url'\nimport { Duplex } from 'node:stream'\nimport Dispatcher from './dispatcher'\n\n/** Performs an HTTP request. */\ndeclare function request<TOpaque = null> (\n  url: string | URL | UrlObject,\n  options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>,\n): Promise<Dispatcher.ResponseData<TOpaque>>\n\n/** A faster version of `request`. */\ndeclare function stream<TOpaque = null> (\n  url: string | URL | UrlObject,\n  options: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path'>,\n  factory: Dispatcher.StreamFactory<TOpaque>\n): Promise<Dispatcher.StreamData<TOpaque>>\n\n/** For easy use with `stream.pipeline`. */\ndeclare function pipeline<TOpaque = null> (\n  url: string | URL | UrlObject,\n  options: { dispatcher?: Dispatcher } & Omit<Dispatcher.PipelineOptions<TOpaque>, 'origin' | 'path'>,\n  handler: Dispatcher.PipelineHandler<TOpaque>\n): Duplex\n\n/** Starts two-way communications with the requested resource. */\ndeclare function connect<TOpaque = null> (\n  url: string | URL | UrlObject,\n  options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.ConnectOptions<TOpaque>, 'origin' | 'path'>\n): Promise<Dispatcher.ConnectData<TOpaque>>\n\n/** Upgrade to a different protocol. */\ndeclare function upgrade (\n  url: string | URL | UrlObject,\n  options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.UpgradeOptions, 'origin' | 'path'>\n): Promise<Dispatcher.UpgradeData>\n\nexport {\n  request,\n  stream,\n  pipeline,\n  connect,\n  upgrade\n}\n"
  },
  {
    "path": "types/balanced-pool.d.ts",
    "content": "import Pool from './pool'\nimport Dispatcher from './dispatcher'\nimport { URL } from 'node:url'\n\nexport default BalancedPool\n\ntype BalancedPoolConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>\n\ndeclare class BalancedPool extends Dispatcher {\n  constructor (url: string | string[] | URL | URL[], options?: Pool.Options)\n\n  addUpstream (upstream: string | URL): BalancedPool\n  removeUpstream (upstream: string | URL): BalancedPool\n  getUpstream (upstream: string | URL): Pool | undefined\n  upstreams: Array<string>\n\n  /** `true` after `pool.close()` has been called. */\n  closed: boolean\n  /** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */\n  destroyed: boolean\n\n  // Override dispatcher APIs.\n  override connect (\n    options: BalancedPoolConnectOptions\n  ): Promise<Dispatcher.ConnectData>\n  override connect (\n    options: BalancedPoolConnectOptions,\n    callback: (err: Error | null, data: Dispatcher.ConnectData) => void\n  ): void\n}\n"
  },
  {
    "path": "types/cache-interceptor.d.ts",
    "content": "import { Readable, Writable } from 'node:stream'\n\nexport default CacheHandler\n\ndeclare namespace CacheHandler {\n  export type CacheMethods = 'GET' | 'HEAD' | 'OPTIONS' | 'TRACE'\n\n  export interface CacheHandlerOptions {\n    store: CacheStore\n\n    cacheByDefault?: number\n\n    type?: CacheOptions['type']\n  }\n\n  export interface CacheOptions {\n    store?: CacheStore\n\n    /**\n     * The methods to cache\n     * Note we can only cache safe methods. Unsafe methods (i.e. PUT, POST)\n     *  invalidate the cache for a origin.\n     * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-respons\n     * @see https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1\n     */\n    methods?: CacheMethods[]\n\n    /**\n     * RFC9111 allows for caching responses that we aren't explicitly told to\n     *  cache or to not cache.\n     * @see https://www.rfc-editor.org/rfc/rfc9111.html#section-3-5\n     * @default undefined\n     */\n    cacheByDefault?: number\n\n    /**\n     * TODO docs\n     * @default 'shared'\n     */\n    type?: 'shared' | 'private'\n\n    /**\n     * Array of origins to cache. Only requests to these origins will be cached.\n     * Supports strings (case insensitive) and RegExp patterns.\n     * @default undefined (cache all origins)\n     */\n    origins?: (string | RegExp)[]\n  }\n\n  export interface CacheControlDirectives {\n    'max-stale'?: number;\n    'min-fresh'?: number;\n    'max-age'?: number;\n    's-maxage'?: number;\n    'stale-while-revalidate'?: number;\n    'stale-if-error'?: number;\n    public?: true;\n    private?: true | string[];\n    'no-store'?: true;\n    'no-cache'?: true | string[];\n    'must-revalidate'?: true;\n    'proxy-revalidate'?: true;\n    immutable?: true;\n    'no-transform'?: true;\n    'must-understand'?: true;\n    'only-if-cached'?: true;\n  }\n\n  export interface CacheKey {\n    origin: string\n    method: string\n    path: string\n    headers?: Record<string, string | string[]>\n  }\n\n  export interface CacheValue {\n    statusCode: number\n    statusMessage: string\n    headers: Record<string, string | string[]>\n    vary?: Record<string, string | string[] | null>\n    etag?: string\n    cacheControlDirectives?: CacheControlDirectives\n    cachedAt: number\n    staleAt: number\n    deleteAt: number\n  }\n\n  export interface DeleteByUri {\n    origin: string\n    method: string\n    path: string\n  }\n\n  type GetResult = {\n    statusCode: number\n    statusMessage: string\n    headers: Record<string, string | string[]>\n    vary?: Record<string, string | string[] | null>\n    etag?: string\n    body?: Readable | Iterable<Buffer> | AsyncIterable<Buffer> | Buffer | Iterable<string> | AsyncIterable<string> | string\n    cacheControlDirectives: CacheControlDirectives,\n    cachedAt: number\n    staleAt: number\n    deleteAt: number\n  }\n\n  /**\n   * Underlying storage provider for cached responses\n   */\n  export interface CacheStore {\n    get(key: CacheKey): GetResult | Promise<GetResult | undefined> | undefined\n\n    createWriteStream(key: CacheKey, val: CacheValue): Writable | undefined\n\n    delete(key: CacheKey): void | Promise<void>\n  }\n\n  export interface MemoryCacheStoreOpts {\n    /**\n       * @default Infinity\n       */\n    maxCount?: number\n\n    /**\n     * @default Infinity\n     */\n    maxSize?: number\n\n    /**\n     * @default Infinity\n     */\n    maxEntrySize?: number\n\n    errorCallback?: (err: Error) => void\n  }\n\n  export class MemoryCacheStore implements CacheStore {\n    constructor (opts?: MemoryCacheStoreOpts)\n\n    get (key: CacheKey): GetResult | Promise<GetResult | undefined> | undefined\n\n    createWriteStream (key: CacheKey, value: CacheValue): Writable | undefined\n\n    delete (key: CacheKey): void | Promise<void>\n  }\n\n  export interface SqliteCacheStoreOpts {\n    /**\n     * Location of the database\n     * @default ':memory:'\n     */\n    location?: string\n\n    /**\n     * @default Infinity\n     */\n    maxCount?: number\n\n    /**\n     * @default Infinity\n     */\n    maxEntrySize?: number\n  }\n\n  export class SqliteCacheStore implements CacheStore {\n    constructor (opts?: SqliteCacheStoreOpts)\n\n    /**\n     * Closes the connection to the database\n     */\n    close (): void\n\n    get (key: CacheKey): GetResult | Promise<GetResult | undefined> | undefined\n\n    createWriteStream (key: CacheKey, value: CacheValue): Writable | undefined\n\n    delete (key: CacheKey): void | Promise<void>\n  }\n}\n"
  },
  {
    "path": "types/cache.d.ts",
    "content": "import type { RequestInfo, Response, Request } from './fetch'\n\nexport interface CacheStorage {\n  match (request: RequestInfo, options?: MultiCacheQueryOptions): Promise<Response | undefined>,\n  has (cacheName: string): Promise<boolean>,\n  open (cacheName: string): Promise<Cache>,\n  delete (cacheName: string): Promise<boolean>,\n  keys (): Promise<string[]>\n}\n\ndeclare const CacheStorage: {\n  prototype: CacheStorage\n  new(): CacheStorage\n}\n\nexport interface Cache {\n  match (request: RequestInfo, options?: CacheQueryOptions): Promise<Response | undefined>,\n  matchAll (request?: RequestInfo, options?: CacheQueryOptions): Promise<readonly Response[]>,\n  add (request: RequestInfo): Promise<undefined>,\n  addAll (requests: RequestInfo[]): Promise<undefined>,\n  put (request: RequestInfo, response: Response): Promise<undefined>,\n  delete (request: RequestInfo, options?: CacheQueryOptions): Promise<boolean>,\n  keys (request?: RequestInfo, options?: CacheQueryOptions): Promise<readonly Request[]>\n}\n\nexport interface CacheQueryOptions {\n  ignoreSearch?: boolean,\n  ignoreMethod?: boolean,\n  ignoreVary?: boolean\n}\n\nexport interface MultiCacheQueryOptions extends CacheQueryOptions {\n  cacheName?: string\n}\n\nexport declare const caches: CacheStorage\n"
  },
  {
    "path": "types/client-stats.d.ts",
    "content": "import Client from './client'\n\nexport default ClientStats\n\ndeclare class ClientStats {\n  constructor (pool: Client)\n  /** If socket has open connection. */\n  connected: boolean\n  /** Number of open socket connections in this client that do not have an active request. */\n  pending: number\n  /** Number of currently active requests of this client. */\n  running: number\n  /** Number of active, pending, or queued requests of this client. */\n  size: number\n}\n"
  },
  {
    "path": "types/client.d.ts",
    "content": "import { URL } from 'node:url'\nimport Dispatcher from './dispatcher'\nimport buildConnector from './connector'\nimport TClientStats from './client-stats'\n\ntype ClientConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>\n\n/**\n * A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.\n */\nexport class Client extends Dispatcher {\n  constructor (url: string | URL, options?: Client.Options)\n  /** Property to get and set the pipelining factor. */\n  pipelining: number\n  /** `true` after `client.close()` has been called. */\n  closed: boolean\n  /** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */\n  destroyed: boolean\n  /** Aggregate stats for a Client. */\n  readonly stats: TClientStats\n\n  // Override dispatcher APIs.\n  override connect (\n    options: ClientConnectOptions\n  ): Promise<Dispatcher.ConnectData>\n  override connect (\n    options: ClientConnectOptions,\n    callback: (err: Error | null, data: Dispatcher.ConnectData) => void\n  ): void\n}\n\nexport declare namespace Client {\n  export interface OptionsInterceptors {\n    Client: readonly Dispatcher.DispatchInterceptor[];\n  }\n  export interface Options {\n    /** TODO */\n    interceptors?: OptionsInterceptors;\n    /** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */\n    maxHeaderSize?: number;\n    /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */\n    headersTimeout?: number;\n    /** @deprecated unsupported socketTimeout, use headersTimeout & bodyTimeout instead */\n    socketTimeout?: never;\n    /** @deprecated unsupported requestTimeout, use headersTimeout & bodyTimeout instead */\n    requestTimeout?: never;\n    /** TODO */\n    connectTimeout?: number;\n    /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */\n    bodyTimeout?: number;\n    /** @deprecated unsupported idleTimeout, use keepAliveTimeout instead */\n    idleTimeout?: never;\n    /** @deprecated unsupported keepAlive, use pipelining=0 instead */\n    keepAlive?: never;\n    /** the timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. Default: `4e3` milliseconds (4s). */\n    keepAliveTimeout?: number;\n    /** @deprecated unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead */\n    maxKeepAliveTimeout?: never;\n    /** the maximum allowed `idleTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Default: `600e3` milliseconds (10min). */\n    keepAliveMaxTimeout?: number;\n    /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */\n    keepAliveTimeoutThreshold?: number;\n    /** TODO */\n    socketPath?: string;\n    /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */\n    pipelining?: number;\n    /** @deprecated use the connect option instead */\n    tls?: never;\n    /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */\n    strictContentLength?: boolean;\n    /** TODO */\n    maxCachedSessions?: number;\n    /** TODO */\n    connect?: Partial<buildConnector.BuildOptions> | buildConnector.connector;\n    /** TODO */\n    maxRequestsPerClient?: number;\n    /** TODO */\n    localAddress?: string;\n    /** Max response body size in bytes, -1 is disabled */\n    maxResponseSize?: number;\n    /** Enables a family autodetection algorithm that loosely implements section 5 of RFC 8305. */\n    autoSelectFamily?: boolean;\n    /** The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. */\n    autoSelectFamilyAttemptTimeout?: number;\n    /**\n     * @description Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.\n     * @default false\n     */\n    allowH2?: boolean;\n    /**\n     * @description Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.\n     * @default 100\n     */\n    maxConcurrentStreams?: number;\n    /**\n     * @description Sets the HTTP/2 stream-level flow-control window size (SETTINGS_INITIAL_WINDOW_SIZE).\n     * @default 262144\n     */\n    initialWindowSize?: number;\n    /**\n     * @description Sets the HTTP/2 connection-level flow-control window size (ClientHttp2Session.setLocalWindowSize).\n     * @default 524288\n     */\n    connectionWindowSize?: number;\n    /**\n     * @description Time interval between PING frames dispatch\n     * @default 60000\n     */\n    pingInterval?: number;\n  }\n  export interface SocketInfo {\n    localAddress?: string\n    localPort?: number\n    remoteAddress?: string\n    remotePort?: number\n    remoteFamily?: string\n    timeout?: number\n    bytesWritten?: number\n    bytesRead?: number\n  }\n}\n\nexport default Client\n"
  },
  {
    "path": "types/connector.d.ts",
    "content": "import { TLSSocket, ConnectionOptions } from 'node:tls'\nimport { IpcNetConnectOpts, Socket, TcpNetConnectOpts } from 'node:net'\n\nexport default buildConnector\ndeclare function buildConnector (options?: buildConnector.BuildOptions): buildConnector.connector\n\ndeclare namespace buildConnector {\n  export type BuildOptions = (ConnectionOptions | TcpNetConnectOpts | IpcNetConnectOpts) & {\n    allowH2?: boolean;\n    maxCachedSessions?: number | null;\n    socketPath?: string | null;\n    timeout?: number | null;\n    port?: number;\n    keepAlive?: boolean | null;\n    keepAliveInitialDelay?: number | null;\n    typeOfService?: number | null;\n  }\n\n  export interface Options {\n    hostname: string\n    host?: string\n    protocol: string\n    port: string\n    servername?: string\n    localAddress?: string | null\n    socketPath?: string | null\n    httpSocket?: Socket\n  }\n\n  export type Callback = (...args: CallbackArgs) => void\n  type CallbackArgs = [null, Socket | TLSSocket] | [Error, null]\n\n  export interface connector {\n    (options: buildConnector.Options, callback: buildConnector.Callback): void\n  }\n}\n"
  },
  {
    "path": "types/content-type.d.ts",
    "content": "/// <reference types=\"node\" />\n\ninterface MIMEType {\n  type: string\n  subtype: string\n  parameters: Map<string, string>\n  essence: string\n}\n\n/**\n * Parse a string to a {@link MIMEType} object. Returns `failure` if the string\n * couldn't be parsed.\n * @see https://mimesniff.spec.whatwg.org/#parse-a-mime-type\n */\nexport function parseMIMEType (input: string): 'failure' | MIMEType\n\n/**\n * Convert a MIMEType object to a string.\n * @see https://mimesniff.spec.whatwg.org/#serialize-a-mime-type\n */\nexport function serializeAMimeType (mimeType: MIMEType): string\n"
  },
  {
    "path": "types/cookies.d.ts",
    "content": "/// <reference types=\"node\" />\n\nimport type { Headers } from './fetch'\n\nexport interface Cookie {\n  name: string\n  value: string\n  expires?: Date | number\n  maxAge?: number\n  domain?: string\n  path?: string\n  secure?: boolean\n  httpOnly?: boolean\n  sameSite?: 'Strict' | 'Lax' | 'None'\n  unparsed?: string[]\n}\n\nexport function deleteCookie (\n  headers: Headers,\n  name: string,\n  attributes?: { name?: string, domain?: string }\n): void\n\nexport function getCookies (headers: Headers): Record<string, string>\n\nexport function getSetCookies (headers: Headers): Cookie[]\n\nexport function setCookie (headers: Headers, cookie: Cookie): void\n\nexport function parseCookie (cookie: string): Cookie | null\n"
  },
  {
    "path": "types/diagnostics-channel.d.ts",
    "content": "import { Socket } from 'node:net'\nimport { URL } from 'node:url'\nimport buildConnector from './connector'\nimport Dispatcher from './dispatcher'\n\ndeclare namespace DiagnosticsChannel {\n  interface Request {\n    origin?: string | URL;\n    completed: boolean;\n    method?: Dispatcher.HttpMethod;\n    path: string;\n    headers: any;\n  }\n  interface Response {\n    statusCode: number;\n    statusText: string;\n    headers: Array<Buffer>;\n  }\n  interface ConnectParams {\n    host: URL['host'];\n    hostname: URL['hostname'];\n    protocol: URL['protocol'];\n    port: URL['port'];\n    servername: string | null;\n  }\n  type Connector = buildConnector.connector\n  export interface RequestCreateMessage {\n    request: Request;\n  }\n  export interface RequestBodySentMessage {\n    request: Request;\n  }\n\n  export interface RequestBodyChunkSentMessage {\n    request: Request;\n    chunk: Uint8Array | string;\n  }\n  export interface RequestBodyChunkReceivedMessage {\n    request: Request;\n    chunk: Buffer;\n  }\n  export interface RequestHeadersMessage {\n    request: Request;\n    response: Response;\n  }\n  export interface RequestTrailersMessage {\n    request: Request;\n    trailers: Array<Buffer>;\n  }\n  export interface RequestErrorMessage {\n    request: Request;\n    error: Error;\n  }\n  export interface ClientSendHeadersMessage {\n    request: Request;\n    headers: string;\n    socket: Socket;\n  }\n  export interface ClientBeforeConnectMessage {\n    connectParams: ConnectParams;\n    connector: Connector;\n  }\n  export interface ClientConnectedMessage {\n    socket: Socket;\n    connectParams: ConnectParams;\n    connector: Connector;\n  }\n  export interface ClientConnectErrorMessage {\n    error: Error;\n    socket: Socket;\n    connectParams: ConnectParams;\n    connector: Connector;\n  }\n}\n"
  },
  {
    "path": "types/dispatcher.d.ts",
    "content": "import { URL } from 'node:url'\nimport { Duplex, Readable, Writable } from 'node:stream'\nimport { EventEmitter } from 'node:events'\nimport { Blob } from 'node:buffer'\nimport { IncomingHttpHeaders } from './header'\nimport BodyReadable from './readable'\nimport { FormData } from './formdata'\nimport Errors from './errors'\nimport { Autocomplete } from './utility'\n\ntype AbortSignal = unknown\n\nexport default Dispatcher\n\nexport type UndiciHeaders = Record<string, string | string[]> | IncomingHttpHeaders | string[] | Iterable<[string, string | string[] | undefined]> | null\n\n/** Dispatcher is the core API used to dispatch requests. */\ndeclare class Dispatcher extends EventEmitter {\n  /** Dispatches a request. This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this. */\n  dispatch (options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean\n  /** Starts two-way communications with the requested resource. */\n  connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ConnectData<TOpaque>) => void): void\n  connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>): Promise<Dispatcher.ConnectData<TOpaque>>\n  /** Compose a chain of dispatchers */\n  compose (dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher\n  compose (...dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher\n  /** Performs an HTTP request. */\n  request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ResponseData<TOpaque>) => void): void\n  request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>): Promise<Dispatcher.ResponseData<TOpaque>>\n  /** For easy use with `stream.pipeline`. */\n  pipeline<TOpaque = null>(options: Dispatcher.PipelineOptions<TOpaque>, handler: Dispatcher.PipelineHandler<TOpaque>): Duplex\n  /** A faster version of `Dispatcher.request`. */\n  stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>, callback: (err: Error | null, data: Dispatcher.StreamData<TOpaque>) => void): void\n  stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>): Promise<Dispatcher.StreamData<TOpaque>>\n  /** Upgrade to a different protocol. */\n  upgrade (options: Dispatcher.UpgradeOptions, callback: (err: Error | null, data: Dispatcher.UpgradeData) => void): void\n  upgrade (options: Dispatcher.UpgradeOptions): Promise<Dispatcher.UpgradeData>\n  /** Closes the client and gracefully waits for enqueued requests to complete before invoking the callback (or returning a promise if no callback is provided). */\n  close (callback: () => void): void\n  close (): Promise<void>\n  /** Destroy the client abruptly with the given err. All the pending and running requests will be asynchronously aborted and error. Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided). Since this operation is asynchronously dispatched there might still be some progress on dispatched requests. */\n  destroy (err: Error | null, callback: () => void): void\n  destroy (callback: () => void): void\n  destroy (err: Error | null): Promise<void>\n  destroy (): Promise<void>\n\n  on (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  on (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  on (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  on (eventName: 'drain', callback: (origin: URL) => void): this\n\n  once (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  once (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  once (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  once (eventName: 'drain', callback: (origin: URL) => void): this\n\n  off (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  off (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  off (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  off (eventName: 'drain', callback: (origin: URL) => void): this\n\n  addListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  addListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  addListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  addListener (eventName: 'drain', callback: (origin: URL) => void): this\n\n  removeListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  removeListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  removeListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  removeListener (eventName: 'drain', callback: (origin: URL) => void): this\n\n  prependListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  prependListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  prependListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  prependListener (eventName: 'drain', callback: (origin: URL) => void): this\n\n  prependOnceListener (eventName: 'connect', callback: (origin: URL, targets: readonly Dispatcher[]) => void): this\n  prependOnceListener (eventName: 'disconnect', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  prependOnceListener (eventName: 'connectionError', callback: (origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void): this\n  prependOnceListener (eventName: 'drain', callback: (origin: URL) => void): this\n\n  listeners (eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]\n  listeners (eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]\n  listeners (eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]\n  listeners (eventName: 'drain'): ((origin: URL) => void)[]\n\n  rawListeners (eventName: 'connect'): ((origin: URL, targets: readonly Dispatcher[]) => void)[]\n  rawListeners (eventName: 'disconnect'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]\n  rawListeners (eventName: 'connectionError'): ((origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError) => void)[]\n  rawListeners (eventName: 'drain'): ((origin: URL) => void)[]\n\n  emit (eventName: 'connect', origin: URL, targets: readonly Dispatcher[]): boolean\n  emit (eventName: 'disconnect', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean\n  emit (eventName: 'connectionError', origin: URL, targets: readonly Dispatcher[], error: Errors.UndiciError): boolean\n  emit (eventName: 'drain', origin: URL): boolean\n}\n\ndeclare namespace Dispatcher {\n  export interface ComposedDispatcher extends Dispatcher { }\n  export type Dispatch = Dispatcher['dispatch']\n  export type DispatcherComposeInterceptor = (dispatch: Dispatch) => Dispatch\n  export interface DispatchOptions {\n    origin?: string | URL;\n    path: string;\n    method: HttpMethod;\n    /** Default: `null` */\n    body?: string | Buffer | Uint8Array | Readable | null | FormData;\n    /** Default: `null` */\n    headers?: UndiciHeaders;\n    /** Query string params to be embedded in the request URL. Default: `null` */\n    query?: Record<string, any>;\n    /** Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline have completed. Default: `true` if `method` is `HEAD` or `GET`. */\n    idempotent?: boolean;\n    /** Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received. Defaults to `method !== 'HEAD'`. */\n    blocking?: boolean;\n    /** The IP Type of Service (ToS) value for the request socket. Must be an integer between 0 and 255. Default: `0` */\n    typeOfService?: number | null;\n    /** Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`. Default: `method === 'CONNECT' || null`. */\n    upgrade?: boolean | string | null;\n    /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers. Defaults to 300 seconds. */\n    headersTimeout?: number | null;\n    /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use 0 to disable it entirely. Defaults to 300 seconds. */\n    bodyTimeout?: number | null;\n    /** Whether the request should stablish a keep-alive or not. Default `false` */\n    reset?: boolean;\n    /** Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server. Defaults to false */\n    throwOnError?: boolean;\n    /** For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server */\n    expectContinue?: boolean;\n  }\n  export interface ConnectOptions<TOpaque = null> {\n    origin: string | URL;\n    path: string;\n    /** Default: `null` */\n    headers?: UndiciHeaders;\n    /** Default: `null` */\n    signal?: AbortSignal | EventEmitter | null;\n    /** This argument parameter is passed through to `ConnectData` */\n    opaque?: TOpaque;\n    /** Default: false */\n    redirectionLimitReached?: boolean;\n    /** Default: `null` */\n    responseHeaders?: 'raw' | null;\n  }\n  export interface RequestOptions<TOpaque = null> extends DispatchOptions {\n    /** Default: `null` */\n    opaque?: TOpaque;\n    /** Default: `null` */\n    signal?: AbortSignal | EventEmitter | null;\n    /** Default: false */\n    redirectionLimitReached?: boolean;\n    /** Default: `null` */\n    onInfo?: (info: { statusCode: number, headers: Record<string, string | string[]> }) => void;\n    /** Default: `null` */\n    responseHeaders?: 'raw' | null;\n    /** Default: `64 KiB` */\n    highWaterMark?: number;\n  }\n  export interface PipelineOptions<TOpaque = null> extends RequestOptions<TOpaque> {\n    /** `true` if the `handler` will return an object stream. Default: `false` */\n    objectMode?: boolean;\n  }\n  export interface UpgradeOptions {\n    path: string;\n    /** Default: `'GET'` */\n    method?: string;\n    /** Default: `null` */\n    headers?: UndiciHeaders;\n    /** A string of comma separated protocols, in descending preference order. Default: `'Websocket'` */\n    protocol?: string;\n    /** Default: `null` */\n    signal?: AbortSignal | EventEmitter | null;\n    /** Default: false */\n    redirectionLimitReached?: boolean;\n    /** Default: `null` */\n    responseHeaders?: 'raw' | null;\n  }\n  export interface ConnectData<TOpaque = null> {\n    statusCode: number;\n    headers: IncomingHttpHeaders;\n    socket: Duplex;\n    opaque: TOpaque;\n  }\n  export interface ResponseData<TOpaque = null> {\n    statusCode: number;\n    statusText: string;\n    headers: IncomingHttpHeaders;\n    body: BodyReadable & BodyMixin;\n    trailers: Record<string, string>;\n    opaque: TOpaque;\n    context: object;\n  }\n  export interface PipelineHandlerData<TOpaque = null> {\n    statusCode: number;\n    headers: IncomingHttpHeaders;\n    opaque: TOpaque;\n    body: BodyReadable;\n    context: object;\n  }\n  export interface StreamData<TOpaque = null> {\n    opaque: TOpaque;\n    trailers: Record<string, string>;\n  }\n  export interface UpgradeData<TOpaque = null> {\n    headers: IncomingHttpHeaders;\n    socket: Duplex;\n    opaque: TOpaque;\n  }\n  export interface StreamFactoryData<TOpaque = null> {\n    statusCode: number;\n    headers: IncomingHttpHeaders;\n    opaque: TOpaque;\n    context: object;\n  }\n  export type StreamFactory<TOpaque = null> = (data: StreamFactoryData<TOpaque>) => Writable\n\n  export interface DispatchController {\n    get aborted(): boolean\n    get paused(): boolean\n    get reason(): Error | null\n    abort(reason: Error): void\n    pause(): void\n    resume(): void\n  }\n\n  export interface DispatchHandler {\n    onRequestStart?(controller: DispatchController, context: any): void;\n    onRequestUpgrade?(controller: DispatchController, statusCode: number, headers: IncomingHttpHeaders, socket: Duplex): void;\n    onResponseStart?(controller: DispatchController, statusCode: number, headers: IncomingHttpHeaders, statusMessage?: string): void;\n    onResponseData?(controller: DispatchController, chunk: Buffer): void;\n    onResponseEnd?(controller: DispatchController, trailers: IncomingHttpHeaders): void;\n    onResponseError?(controller: DispatchController, error: Error): void;\n\n    /** Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails. */\n    /** @deprecated */\n    onConnect?(abort: (err?: Error) => void): void;\n    /** Invoked when an error has occurred. */\n    /** @deprecated */\n    onError?(err: Error): void;\n    /** Invoked when request is upgraded either due to a `Upgrade` header or `CONNECT` method. */\n    /** @deprecated */\n    onUpgrade?(statusCode: number, headers: Buffer[] | string[] | null, socket: Duplex): void;\n    /** Invoked when response is received, before headers have been read. **/\n    /** @deprecated */\n    onResponseStarted?(): void;\n    /** Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. */\n    /** @deprecated */\n    onHeaders?(statusCode: number, headers: Buffer[], resume: () => void, statusText: string): boolean;\n    /** Invoked when response payload data is received. */\n    /** @deprecated */\n    onData?(chunk: Buffer): boolean;\n    /** Invoked when response payload and trailers have been received and the request has completed. */\n    /** @deprecated */\n    onComplete?(trailers: string[] | null): void;\n    /** Invoked when a body chunk is sent to the server. May be invoked multiple times for chunked requests */\n    /** @deprecated */\n    onBodySent?(chunkSize: number, totalBytesSent: number): void;\n  }\n  export type PipelineHandler<TOpaque = null> = (data: PipelineHandlerData<TOpaque>) => Readable\n  export type HttpMethod = Autocomplete<'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH'>\n\n  /**\n   * @link https://fetch.spec.whatwg.org/#body-mixin\n   */\n  interface BodyMixin {\n    readonly body?: never;\n    readonly bodyUsed: boolean;\n    arrayBuffer(): Promise<ArrayBuffer>;\n    blob(): Promise<Blob>;\n    bytes(): Promise<Uint8Array>;\n    formData(): Promise<never>;\n    json(): Promise<unknown>;\n    text(): Promise<string>;\n  }\n\n  export interface DispatchInterceptor {\n    (dispatch: Dispatch): Dispatch\n  }\n}\n"
  },
  {
    "path": "types/env-http-proxy-agent.d.ts",
    "content": "import Agent from './agent'\nimport ProxyAgent from './proxy-agent'\nimport Dispatcher from './dispatcher'\n\nexport default EnvHttpProxyAgent\n\ndeclare class EnvHttpProxyAgent extends Dispatcher {\n  constructor (opts?: EnvHttpProxyAgent.Options)\n\n  dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean\n}\n\ndeclare namespace EnvHttpProxyAgent {\n  export interface Options extends Omit<ProxyAgent.Options, 'uri'> {\n    /** Overrides the value of the HTTP_PROXY environment variable  */\n    httpProxy?: string;\n    /** Overrides the value of the HTTPS_PROXY environment variable  */\n    httpsProxy?: string;\n    /** Overrides the value of the NO_PROXY environment variable  */\n    noProxy?: string;\n  }\n}\n"
  },
  {
    "path": "types/errors.d.ts",
    "content": "import { IncomingHttpHeaders } from './header'\nimport Client from './client'\n\nexport default Errors\n\ndeclare namespace Errors {\n  export class UndiciError extends Error {\n    name: string\n    code: string\n  }\n\n  /** Connect timeout error. */\n  export class ConnectTimeoutError extends UndiciError {\n    name: 'ConnectTimeoutError'\n    code: 'UND_ERR_CONNECT_TIMEOUT'\n  }\n\n  /** A header exceeds the `headersTimeout` option. */\n  export class HeadersTimeoutError extends UndiciError {\n    name: 'HeadersTimeoutError'\n    code: 'UND_ERR_HEADERS_TIMEOUT'\n  }\n\n  /** Headers overflow error. */\n  export class HeadersOverflowError extends UndiciError {\n    name: 'HeadersOverflowError'\n    code: 'UND_ERR_HEADERS_OVERFLOW'\n  }\n\n  /** A body exceeds the `bodyTimeout` option. */\n  export class BodyTimeoutError extends UndiciError {\n    name: 'BodyTimeoutError'\n    code: 'UND_ERR_BODY_TIMEOUT'\n  }\n\n  export class ResponseError extends UndiciError {\n    constructor (\n      message: string,\n      code: number,\n      options: {\n        headers?: IncomingHttpHeaders | string[] | null,\n        body?: null | Record<string, any> | string\n      }\n    )\n    name: 'ResponseError'\n    code: 'UND_ERR_RESPONSE'\n    statusCode: number\n    body: null | Record<string, any> | string\n    headers: IncomingHttpHeaders | string[] | null\n  }\n\n  /** Passed an invalid argument. */\n  export class InvalidArgumentError extends UndiciError {\n    name: 'InvalidArgumentError'\n    code: 'UND_ERR_INVALID_ARG'\n  }\n\n  /** Returned an invalid value. */\n  export class InvalidReturnValueError extends UndiciError {\n    name: 'InvalidReturnValueError'\n    code: 'UND_ERR_INVALID_RETURN_VALUE'\n  }\n\n  /** The request has been aborted by the user. */\n  export class RequestAbortedError extends UndiciError {\n    name: 'AbortError'\n    code: 'UND_ERR_ABORTED'\n  }\n\n  /** Expected error with reason. */\n  export class InformationalError extends UndiciError {\n    name: 'InformationalError'\n    code: 'UND_ERR_INFO'\n  }\n\n  /** Request body length does not match content-length header. */\n  export class RequestContentLengthMismatchError extends UndiciError {\n    name: 'RequestContentLengthMismatchError'\n    code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'\n  }\n\n  /** Response body length does not match content-length header. */\n  export class ResponseContentLengthMismatchError extends UndiciError {\n    name: 'ResponseContentLengthMismatchError'\n    code: 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'\n  }\n\n  /** Trying to use a destroyed client. */\n  export class ClientDestroyedError extends UndiciError {\n    name: 'ClientDestroyedError'\n    code: 'UND_ERR_DESTROYED'\n  }\n\n  /** Trying to use a closed client. */\n  export class ClientClosedError extends UndiciError {\n    name: 'ClientClosedError'\n    code: 'UND_ERR_CLOSED'\n  }\n\n  /** There is an error with the socket. */\n  export class SocketError extends UndiciError {\n    name: 'SocketError'\n    code: 'UND_ERR_SOCKET'\n    socket: Client.SocketInfo | null\n  }\n\n  /** Encountered unsupported functionality. */\n  export class NotSupportedError extends UndiciError {\n    name: 'NotSupportedError'\n    code: 'UND_ERR_NOT_SUPPORTED'\n  }\n\n  /** No upstream has been added to the BalancedPool. */\n  export class BalancedPoolMissingUpstreamError extends UndiciError {\n    name: 'MissingUpstreamError'\n    code: 'UND_ERR_BPL_MISSING_UPSTREAM'\n  }\n\n  export class HTTPParserError extends UndiciError {\n    name: 'HTTPParserError'\n    code: string\n  }\n\n  /** The response exceed the length allowed. */\n  export class ResponseExceededMaxSizeError extends UndiciError {\n    name: 'ResponseExceededMaxSizeError'\n    code: 'UND_ERR_RES_EXCEEDED_MAX_SIZE'\n  }\n\n  export class RequestRetryError extends UndiciError {\n    constructor (\n      message: string,\n      statusCode: number,\n      headers?: IncomingHttpHeaders | string[] | null,\n      body?: null | Record<string, any> | string\n    )\n    name: 'RequestRetryError'\n    code: 'UND_ERR_REQ_RETRY'\n    statusCode: number\n    data: {\n      count: number;\n    }\n\n    headers: Record<string, string | string[]>\n  }\n\n  export class SecureProxyConnectionError extends UndiciError {\n    constructor (\n      cause?: Error,\n      message?: string,\n      options?: Record<any, any>\n    )\n    name: 'SecureProxyConnectionError'\n    code: 'UND_ERR_PRX_TLS'\n  }\n\n  export class MaxOriginsReachedError extends UndiciError {\n    name: 'MaxOriginsReachedError'\n    code: 'UND_ERR_MAX_ORIGINS_REACHED'\n  }\n\n  /** SOCKS5 proxy related error. */\n  export class Socks5ProxyError extends UndiciError {\n    constructor (\n      message?: string,\n      code?: string\n    )\n    name: 'Socks5ProxyError'\n    code: string\n  }\n\n  /** WebSocket decompressed message exceeded maximum size. */\n  export class MessageSizeExceededError extends UndiciError {\n    name: 'MessageSizeExceededError'\n    code: 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED'\n  }\n}\n"
  },
  {
    "path": "types/eventsource.d.ts",
    "content": "import { MessageEvent, ErrorEvent } from './websocket'\nimport Dispatcher from './dispatcher'\n\nimport {\n  EventListenerOptions,\n  AddEventListenerOptions,\n  EventListenerOrEventListenerObject\n} from './patch'\n\ninterface EventSourceEventMap {\n  error: ErrorEvent\n  message: MessageEvent\n  open: Event\n}\n\ninterface EventSource extends EventTarget {\n  close(): void\n  readonly CLOSED: 2\n  readonly CONNECTING: 0\n  readonly OPEN: 1\n  onerror: ((this: EventSource, ev: ErrorEvent) => any) | null\n  onmessage: ((this: EventSource, ev: MessageEvent) => any) | null\n  onopen: ((this: EventSource, ev: Event) => any) | null\n  readonly readyState: 0 | 1 | 2\n  readonly url: string\n  readonly withCredentials: boolean\n\n  addEventListener<K extends keyof EventSourceEventMap>(\n    type: K,\n    listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,\n    options?: boolean | AddEventListenerOptions\n  ): void\n  addEventListener(\n    type: string,\n    listener: EventListenerOrEventListenerObject,\n    options?: boolean | AddEventListenerOptions\n  ): void\n  removeEventListener<K extends keyof EventSourceEventMap>(\n    type: K,\n    listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,\n    options?: boolean | EventListenerOptions\n  ): void\n  removeEventListener(\n    type: string,\n    listener: EventListenerOrEventListenerObject,\n    options?: boolean | EventListenerOptions\n  ): void\n}\n\nexport declare const EventSource: {\n  prototype: EventSource\n  new (url: string | URL, init?: EventSourceInit): EventSource\n  readonly CLOSED: 2\n  readonly CONNECTING: 0\n  readonly OPEN: 1\n}\n\ninterface EventSourceInit {\n  withCredentials?: boolean\n  // @deprecated use `node.dispatcher` instead\n  dispatcher?: Dispatcher\n  node?: {\n    dispatcher?: Dispatcher\n    reconnectionTime?: number\n  }\n}\n"
  },
  {
    "path": "types/fetch.d.ts",
    "content": "// based on https://github.com/Ethan-Arrowood/undici-fetch/blob/249269714db874351589d2d364a0645d5160ae71/index.d.ts (MIT license)\n// and https://github.com/node-fetch/node-fetch/blob/914ce6be5ec67a8bab63d68510aabf07cb818b6d/index.d.ts (MIT license)\n/// <reference types=\"node\" />\n\nimport { Blob } from 'node:buffer'\nimport { URL, URLSearchParams } from 'node:url'\nimport { ReadableStream } from 'node:stream/web'\nimport { FormData } from './formdata'\nimport { HeaderRecord } from './header'\nimport Dispatcher from './dispatcher'\n\nexport type RequestInfo = string | URL | Request\n\nexport declare function fetch (\n  input: RequestInfo,\n  init?: RequestInit\n): Promise<Response>\n\nexport type BodyInit =\n  | ArrayBuffer\n  | AsyncIterable<Uint8Array>\n  | Blob\n  | FormData\n  | Iterable<Uint8Array>\n  | NodeJS.ArrayBufferView\n  | URLSearchParams\n  | null\n  | string\n\nexport class BodyMixin {\n  readonly body: ReadableStream | null\n  readonly bodyUsed: boolean\n\n  readonly arrayBuffer: () => Promise<ArrayBuffer>\n  readonly blob: () => Promise<Blob>\n  readonly bytes: () => Promise<Uint8Array>\n  /**\n   * @deprecated This method is not recommended for parsing multipart/form-data bodies in server environments.\n   * It is recommended to use a library such as [@fastify/busboy](https://www.npmjs.com/package/@fastify/busboy) as follows:\n   *\n   * @example\n   * ```js\n   * import { Busboy } from '@fastify/busboy'\n   * import { Readable } from 'node:stream'\n   *\n   * const response = await fetch('...')\n   * const busboy = new Busboy({ headers: { 'content-type': response.headers.get('content-type') } })\n   *\n   * // handle events emitted from `busboy`\n   *\n   * Readable.fromWeb(response.body).pipe(busboy)\n   * ```\n   */\n  readonly formData: () => Promise<FormData>\n  readonly json: () => Promise<unknown>\n  readonly text: () => Promise<string>\n}\n\nexport interface SpecIterator<T, TReturn = any, TNext = undefined> {\n  next(...args: [] | [TNext]): IteratorResult<T, TReturn>;\n}\n\nexport interface SpecIterableIterator<T> extends SpecIterator<T> {\n  [Symbol.iterator](): SpecIterableIterator<T>;\n}\n\nexport interface SpecIterable<T> {\n  [Symbol.iterator](): SpecIterator<T>;\n}\n\nexport type HeadersInit = [string, string][] | HeaderRecord | Headers\n\nexport declare class Headers implements SpecIterable<[string, string]> {\n  constructor (init?: HeadersInit)\n  readonly append: (name: string, value: string) => void\n  readonly delete: (name: string) => void\n  readonly get: (name: string) => string | null\n  readonly has: (name: string) => boolean\n  readonly set: (name: string, value: string) => void\n  readonly getSetCookie: () => string[]\n  readonly forEach: (\n    callbackfn: (value: string, key: string, iterable: Headers) => void,\n    thisArg?: unknown\n  ) => void\n\n  readonly keys: () => SpecIterableIterator<string>\n  readonly values: () => SpecIterableIterator<string>\n  readonly entries: () => SpecIterableIterator<[string, string]>\n  readonly [Symbol.iterator]: () => SpecIterableIterator<[string, string]>\n}\n\nexport type RequestCache =\n  | 'default'\n  | 'force-cache'\n  | 'no-cache'\n  | 'no-store'\n  | 'only-if-cached'\n  | 'reload'\n\nexport type RequestCredentials = 'omit' | 'include' | 'same-origin'\n\ntype RequestDestination =\n  | ''\n  | 'audio'\n  | 'audioworklet'\n  | 'document'\n  | 'embed'\n  | 'font'\n  | 'image'\n  | 'manifest'\n  | 'object'\n  | 'paintworklet'\n  | 'report'\n  | 'script'\n  | 'sharedworker'\n  | 'style'\n  | 'track'\n  | 'video'\n  | 'worker'\n  | 'xslt'\n\nexport interface RequestInit {\n  body?: BodyInit | null\n  cache?: RequestCache\n  credentials?: RequestCredentials\n  dispatcher?: Dispatcher\n  duplex?: RequestDuplex\n  headers?: HeadersInit\n  integrity?: string\n  keepalive?: boolean\n  method?: string\n  mode?: RequestMode\n  redirect?: RequestRedirect\n  referrer?: string\n  referrerPolicy?: ReferrerPolicy\n  signal?: AbortSignal | null\n  window?: null\n}\n\nexport type ReferrerPolicy =\n  | ''\n  | 'no-referrer'\n  | 'no-referrer-when-downgrade'\n  | 'origin'\n  | 'origin-when-cross-origin'\n  | 'same-origin'\n  | 'strict-origin'\n  | 'strict-origin-when-cross-origin'\n  | 'unsafe-url'\n\nexport type RequestMode = 'cors' | 'navigate' | 'no-cors' | 'same-origin'\n\nexport type RequestRedirect = 'error' | 'follow' | 'manual'\n\nexport type RequestDuplex = 'half'\n\nexport declare class Request extends BodyMixin {\n  constructor (input: RequestInfo, init?: RequestInit)\n\n  readonly cache: RequestCache\n  readonly credentials: RequestCredentials\n  readonly destination: RequestDestination\n  readonly headers: Headers\n  readonly integrity: string\n  readonly method: string\n  readonly mode: RequestMode\n  readonly redirect: RequestRedirect\n  readonly referrer: string\n  readonly referrerPolicy: ReferrerPolicy\n  readonly url: string\n\n  readonly keepalive: boolean\n  readonly signal: AbortSignal\n  readonly duplex: RequestDuplex\n\n  readonly clone: () => Request\n}\n\nexport interface ResponseInit {\n  readonly status?: number\n  readonly statusText?: string\n  readonly headers?: HeadersInit\n}\n\nexport type ResponseType =\n  | 'basic'\n  | 'cors'\n  | 'default'\n  | 'error'\n  | 'opaque'\n  | 'opaqueredirect'\n\nexport type ResponseRedirectStatus = 301 | 302 | 303 | 307 | 308\n\nexport declare class Response extends BodyMixin {\n  constructor (body?: BodyInit, init?: ResponseInit)\n\n  readonly headers: Headers\n  readonly ok: boolean\n  readonly status: number\n  readonly statusText: string\n  readonly type: ResponseType\n  readonly url: string\n  readonly redirected: boolean\n\n  readonly clone: () => Response\n\n  static error (): Response\n  static json (data: any, init?: ResponseInit): Response\n  static redirect (url: string | URL, status?: ResponseRedirectStatus): Response\n}\n"
  },
  {
    "path": "types/formdata.d.ts",
    "content": "// Based on https://github.com/octet-stream/form-data/blob/2d0f0dc371517444ce1f22cdde13f51995d0953a/lib/FormData.ts (MIT)\n/// <reference types=\"node\" />\n\nimport { File } from 'node:buffer'\nimport { SpecIterableIterator } from './fetch'\n\n/**\n * A `string` or `File` that represents a single value from a set of `FormData` key-value pairs.\n */\ndeclare type FormDataEntryValue = string | File\n\n/**\n * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using fetch().\n */\nexport declare class FormData {\n  /**\n   * Appends a new value onto an existing key inside a FormData object,\n   * or adds the key if it does not already exist.\n   *\n   * The difference between `set()` and `append()` is that if the specified key already exists, `set()` will overwrite all existing values with the new one, whereas `append()` will append the new value onto the end of the existing set of values.\n   *\n   * @param name The name of the field whose data is contained in `value`.\n   * @param value The field's value. This can be [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n    or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File). If none of these are specified the value is converted to a string.\n   * @param fileName The filename reported to the server, when a Blob or File is passed as the second parameter. The default filename for Blob objects is \"blob\". The default filename for File objects is the file's filename.\n   */\n  append (name: string, value: unknown, fileName?: string): void\n\n  /**\n   * Set a new value for an existing key inside FormData,\n   * or add the new field if it does not already exist.\n   *\n   * @param name The name of the field whose data is contained in `value`.\n   * @param value The field's value. This can be [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n    or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File). If none of these are specified the value is converted to a string.\n   * @param fileName The filename reported to the server, when a Blob or File is passed as the second parameter. The default filename for Blob objects is \"blob\". The default filename for File objects is the file's filename.\n   *\n   */\n  set (name: string, value: unknown, fileName?: string): void\n\n  /**\n   * Returns the first value associated with a given key from within a `FormData` object.\n   * If you expect multiple values and want all of them, use the `getAll()` method instead.\n   *\n   * @param {string} name A name of the value you want to retrieve.\n   *\n   * @returns A `FormDataEntryValue` containing the value. If the key doesn't exist, the method returns null.\n   */\n  get (name: string): FormDataEntryValue | null\n\n  /**\n   * Returns all the values associated with a given key from within a `FormData` object.\n   *\n   * @param {string} name A name of the value you want to retrieve.\n   *\n   * @returns An array of `FormDataEntryValue` whose key matches the value passed in the `name` parameter. If the key doesn't exist, the method returns an empty list.\n   */\n  getAll (name: string): FormDataEntryValue[]\n\n  /**\n   * Returns a boolean stating whether a `FormData` object contains a certain key.\n   *\n   * @param name A string representing the name of the key you want to test for.\n   *\n   * @return A boolean value.\n   */\n  has (name: string): boolean\n\n  /**\n   * Deletes a key and its value(s) from a `FormData` object.\n   *\n   * @param name The name of the key you want to delete.\n   */\n  delete (name: string): void\n\n  /**\n   * Executes given callback function for each field of the FormData instance\n   */\n  forEach: (\n    callbackfn: (value: FormDataEntryValue, key: string, iterable: FormData) => void,\n    thisArg?: unknown\n  ) => void\n\n  /**\n   * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object.\n   * Each key is a `string`.\n   */\n  keys: () => SpecIterableIterator<string>\n\n  /**\n   * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all values contained in this object `FormData` object.\n   * Each value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).\n   */\n  values: () => SpecIterableIterator<FormDataEntryValue>\n\n  /**\n   * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs.\n   * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).\n   */\n  entries: () => SpecIterableIterator<[string, FormDataEntryValue]>\n\n  /**\n   * An alias for FormData#entries()\n   */\n  [Symbol.iterator]: () => SpecIterableIterator<[string, FormDataEntryValue]>\n\n  readonly [Symbol.toStringTag]: string\n}\n"
  },
  {
    "path": "types/global-dispatcher.d.ts",
    "content": "import Dispatcher from './dispatcher'\n\ndeclare function setGlobalDispatcher<DispatcherImplementation extends Dispatcher> (dispatcher: DispatcherImplementation): void\ndeclare function getGlobalDispatcher (): Dispatcher\n\nexport {\n  getGlobalDispatcher,\n  setGlobalDispatcher\n}\n"
  },
  {
    "path": "types/global-origin.d.ts",
    "content": "declare function setGlobalOrigin (origin: string | URL | undefined): void\ndeclare function getGlobalOrigin (): URL | undefined\n\nexport {\n  setGlobalOrigin,\n  getGlobalOrigin\n}\n"
  },
  {
    "path": "types/h2c-client.d.ts",
    "content": "import { URL } from 'node:url'\nimport Dispatcher from './dispatcher'\nimport buildConnector from './connector'\n\ntype H2ClientOptions = Omit<Dispatcher.ConnectOptions, 'origin'>\n\n/**\n * A basic H2C client, mapped on top a single TCP connection. Pipelining is disabled by default.\n */\nexport class H2CClient extends Dispatcher {\n  constructor (url: string | URL, options?: H2CClient.Options)\n  /** Property to get and set the pipelining factor. */\n  pipelining: number\n  /** `true` after `client.close()` has been called. */\n  closed: boolean\n  /** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */\n  destroyed: boolean\n\n  // Override dispatcher APIs.\n  override connect (\n    options: H2ClientOptions\n  ): Promise<Dispatcher.ConnectData>\n  override connect (\n    options: H2ClientOptions,\n    callback: (err: Error | null, data: Dispatcher.ConnectData) => void\n  ): void\n}\n\nexport declare namespace H2CClient {\n  export interface Options {\n    /** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */\n    maxHeaderSize?: number;\n    /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */\n    headersTimeout?: number;\n    /** TODO */\n    connectTimeout?: number;\n    /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */\n    bodyTimeout?: number;\n    /** the timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. Default: `4e3` milliseconds (4s). */\n    keepAliveTimeout?: number;\n    /** the maximum allowed `idleTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Default: `600e3` milliseconds (10min). */\n    keepAliveMaxTimeout?: number;\n    /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */\n    keepAliveTimeoutThreshold?: number;\n    /** TODO */\n    socketPath?: string;\n    /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */\n    pipelining?: number;\n    /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */\n    strictContentLength?: boolean;\n    /** TODO */\n    maxCachedSessions?: number;\n    /** TODO */\n    connect?: Omit<Partial<buildConnector.BuildOptions>, 'allowH2'> | buildConnector.connector;\n    /** TODO */\n    maxRequestsPerClient?: number;\n    /** TODO */\n    localAddress?: string;\n    /** Max response body size in bytes, -1 is disabled */\n    maxResponseSize?: number;\n    /** Enables a family autodetection algorithm that loosely implements section 5 of RFC 8305. */\n    autoSelectFamily?: boolean;\n    /** The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. */\n    autoSelectFamilyAttemptTimeout?: number;\n    /**\n     * @description Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.\n     * @default 100\n    */\n    maxConcurrentStreams?: number\n  }\n}\n\nexport default H2CClient\n"
  },
  {
    "path": "types/handlers.d.ts",
    "content": "import Dispatcher from './dispatcher'\n\nexport declare class RedirectHandler implements Dispatcher.DispatchHandler {\n  constructor (\n    dispatch: Dispatcher.Dispatch,\n    maxRedirections: number,\n    opts: Dispatcher.DispatchOptions,\n    handler: Dispatcher.DispatchHandler,\n    redirectionLimitReached: boolean\n  )\n}\n\nexport declare class DecoratorHandler implements Dispatcher.DispatchHandler {\n  constructor (handler: Dispatcher.DispatchHandler)\n}\n"
  },
  {
    "path": "types/header.d.ts",
    "content": "import { Autocomplete } from './utility'\n\n/**\n * The header type declaration of `undici`.\n */\nexport type IncomingHttpHeaders = Record<string, string | string[] | undefined>\n\ntype HeaderNames = Autocomplete<\n  | 'Accept'\n  | 'Accept-CH'\n  | 'Accept-Charset'\n  | 'Accept-Encoding'\n  | 'Accept-Language'\n  | 'Accept-Patch'\n  | 'Accept-Post'\n  | 'Accept-Ranges'\n  | 'Access-Control-Allow-Credentials'\n  | 'Access-Control-Allow-Headers'\n  | 'Access-Control-Allow-Methods'\n  | 'Access-Control-Allow-Origin'\n  | 'Access-Control-Expose-Headers'\n  | 'Access-Control-Max-Age'\n  | 'Access-Control-Request-Headers'\n  | 'Access-Control-Request-Method'\n  | 'Age'\n  | 'Allow'\n  | 'Alt-Svc'\n  | 'Alt-Used'\n  | 'Authorization'\n  | 'Cache-Control'\n  | 'Clear-Site-Data'\n  | 'Connection'\n  | 'Content-Disposition'\n  | 'Content-Encoding'\n  | 'Content-Language'\n  | 'Content-Length'\n  | 'Content-Location'\n  | 'Content-Range'\n  | 'Content-Security-Policy'\n  | 'Content-Security-Policy-Report-Only'\n  | 'Content-Type'\n  | 'Cookie'\n  | 'Cross-Origin-Embedder-Policy'\n  | 'Cross-Origin-Opener-Policy'\n  | 'Cross-Origin-Resource-Policy'\n  | 'Date'\n  | 'Device-Memory'\n  | 'ETag'\n  | 'Expect'\n  | 'Expect-CT'\n  | 'Expires'\n  | 'Forwarded'\n  | 'From'\n  | 'Host'\n  | 'If-Match'\n  | 'If-Modified-Since'\n  | 'If-None-Match'\n  | 'If-Range'\n  | 'If-Unmodified-Since'\n  | 'Keep-Alive'\n  | 'Last-Modified'\n  | 'Link'\n  | 'Location'\n  | 'Max-Forwards'\n  | 'Origin'\n  | 'Permissions-Policy'\n  | 'Priority'\n  | 'Proxy-Authenticate'\n  | 'Proxy-Authorization'\n  | 'Range'\n  | 'Referer'\n  | 'Referrer-Policy'\n  | 'Retry-After'\n  | 'Sec-Fetch-Dest'\n  | 'Sec-Fetch-Mode'\n  | 'Sec-Fetch-Site'\n  | 'Sec-Fetch-User'\n  | 'Sec-Purpose'\n  | 'Sec-WebSocket-Accept'\n  | 'Server'\n  | 'Server-Timing'\n  | 'Service-Worker-Navigation-Preload'\n  | 'Set-Cookie'\n  | 'SourceMap'\n  | 'Strict-Transport-Security'\n  | 'TE'\n  | 'Timing-Allow-Origin'\n  | 'Trailer'\n  | 'Transfer-Encoding'\n  | 'Upgrade'\n  | 'Upgrade-Insecure-Requests'\n  | 'User-Agent'\n  | 'Vary'\n  | 'Via'\n  | 'WWW-Authenticate'\n  | 'X-Content-Type-Options'\n  | 'X-Frame-Options'\n>\n\ntype IANARegisteredMimeType = Autocomplete<\n  | 'audio/aac'\n  | 'video/x-msvideo'\n  | 'image/avif'\n  | 'video/av1'\n  | 'application/octet-stream'\n  | 'image/bmp'\n  | 'text/css'\n  | 'text/csv'\n  | 'application/vnd.ms-fontobject'\n  | 'application/epub+zip'\n  | 'image/gif'\n  | 'application/gzip'\n  | 'text/html'\n  | 'image/x-icon'\n  | 'text/calendar'\n  | 'image/jpeg'\n  | 'text/javascript'\n  | 'application/json'\n  | 'application/ld+json'\n  | 'audio/x-midi'\n  | 'audio/mpeg'\n  | 'video/mp4'\n  | 'video/mpeg'\n  | 'audio/ogg'\n  | 'video/ogg'\n  | 'application/ogg'\n  | 'audio/opus'\n  | 'font/otf'\n  | 'application/pdf'\n  | 'image/png'\n  | 'application/rtf'\n  | 'image/svg+xml'\n  | 'image/tiff'\n  | 'video/mp2t'\n  | 'font/ttf'\n  | 'text/plain'\n  | 'application/wasm'\n  | 'video/webm'\n  | 'audio/webm'\n  | 'image/webp'\n  | 'font/woff'\n  | 'font/woff2'\n  | 'application/xhtml+xml'\n  | 'application/xml'\n  | 'application/zip'\n  | 'video/3gpp'\n  | 'video/3gpp2'\n  | 'model/gltf+json'\n  | 'model/gltf-binary'\n>\n\ntype KnownHeaderValues = {\n  'content-type': IANARegisteredMimeType\n}\n\nexport type HeaderRecord = {\n  [K in HeaderNames | Lowercase<HeaderNames>]?: Lowercase<K> extends keyof KnownHeaderValues\n    ? KnownHeaderValues[Lowercase<K>]\n    : string\n}\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "import Dispatcher from './dispatcher'\nimport { setGlobalDispatcher, getGlobalDispatcher } from './global-dispatcher'\nimport { setGlobalOrigin, getGlobalOrigin } from './global-origin'\nimport Pool from './pool'\nimport { RedirectHandler, DecoratorHandler } from './handlers'\n\nimport BalancedPool from './balanced-pool'\nimport RoundRobinPool from './round-robin-pool'\nimport Client from './client'\nimport H2CClient from './h2c-client'\nimport buildConnector from './connector'\nimport errors from './errors'\nimport Agent from './agent'\nimport MockClient from './mock-client'\nimport MockPool from './mock-pool'\nimport MockAgent from './mock-agent'\nimport { SnapshotAgent } from './snapshot-agent'\nimport { MockCallHistory, MockCallHistoryLog } from './mock-call-history'\nimport mockErrors from './mock-errors'\nimport ProxyAgent from './proxy-agent'\nimport Socks5ProxyAgent from './socks5-proxy-agent'\nimport EnvHttpProxyAgent from './env-http-proxy-agent'\nimport RetryHandler from './retry-handler'\nimport RetryAgent from './retry-agent'\nimport { request, pipeline, stream, connect, upgrade } from './api'\nimport interceptors from './interceptors'\n\nimport CacheInterceptor from './cache-interceptor'\ndeclare const cacheStores: {\n  MemoryCacheStore: typeof CacheInterceptor.MemoryCacheStore;\n  SqliteCacheStore: typeof CacheInterceptor.SqliteCacheStore;\n}\n\nexport * from './util'\nexport * from './cookies'\nexport * from './eventsource'\nexport * from './fetch'\nexport * from './formdata'\nexport * from './diagnostics-channel'\nexport * from './websocket'\nexport * from './content-type'\nexport * from './cache'\nexport { Interceptable } from './mock-interceptor'\n\ndeclare function globalThisInstall (): void\n\nexport { Dispatcher, BalancedPool, RoundRobinPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, interceptors, cacheStores, MockClient, MockPool, MockAgent, SnapshotAgent, MockCallHistory, MockCallHistoryLog, mockErrors, ProxyAgent, Socks5ProxyAgent, EnvHttpProxyAgent, RedirectHandler, DecoratorHandler, RetryHandler, RetryAgent, H2CClient, globalThisInstall as install }\nexport default Undici\n\ndeclare namespace Undici {\n  const Dispatcher: typeof import('./dispatcher').default\n  const Pool: typeof import('./pool').default\n  const RedirectHandler: typeof import ('./handlers').RedirectHandler\n  const DecoratorHandler: typeof import ('./handlers').DecoratorHandler\n  const RetryHandler: typeof import ('./retry-handler').default\n  const BalancedPool: typeof import('./balanced-pool').default\n  const RoundRobinPool: typeof import('./round-robin-pool').default\n  const Client: typeof import('./client').default\n  const H2CClient: typeof import('./h2c-client').default\n  const buildConnector: typeof import('./connector').default\n  const errors: typeof import('./errors').default\n  const Agent: typeof import('./agent').default\n  const setGlobalDispatcher: typeof import('./global-dispatcher').setGlobalDispatcher\n  const getGlobalDispatcher: typeof import('./global-dispatcher').getGlobalDispatcher\n  const request: typeof import('./api').request\n  const stream: typeof import('./api').stream\n  const pipeline: typeof import('./api').pipeline\n  const connect: typeof import('./api').connect\n  const upgrade: typeof import('./api').upgrade\n  const MockClient: typeof import('./mock-client').default\n  const MockPool: typeof import('./mock-pool').default\n  const MockAgent: typeof import('./mock-agent').default\n  const SnapshotAgent: typeof import('./snapshot-agent').SnapshotAgent\n  const MockCallHistory: typeof import('./mock-call-history').MockCallHistory\n  const MockCallHistoryLog: typeof import('./mock-call-history').MockCallHistoryLog\n  const mockErrors: typeof import('./mock-errors').default\n  const ProxyAgent: typeof import('./proxy-agent').default\n  const Socks5ProxyAgent: typeof import('./socks5-proxy-agent').default\n  const fetch: typeof import('./fetch').fetch\n  const Headers: typeof import('./fetch').Headers\n  const Response: typeof import('./fetch').Response\n  const Request: typeof import('./fetch').Request\n  const FormData: typeof import('./formdata').FormData\n  const caches: typeof import('./cache').caches\n  const interceptors: typeof import('./interceptors').default\n  const cacheStores: {\n    MemoryCacheStore: typeof import('./cache-interceptor').default.MemoryCacheStore,\n    SqliteCacheStore: typeof import('./cache-interceptor').default.SqliteCacheStore\n  }\n  const install: typeof globalThisInstall\n}\n"
  },
  {
    "path": "types/interceptors.d.ts",
    "content": "import CacheHandler from './cache-interceptor'\nimport Dispatcher from './dispatcher'\nimport RetryHandler from './retry-handler'\nimport { LookupOptions } from 'node:dns'\n\nexport default Interceptors\n\ndeclare namespace Interceptors {\n  export type DumpInterceptorOpts = { maxSize?: number }\n  export type RetryInterceptorOpts = RetryHandler.RetryOptions\n  export type RedirectInterceptorOpts = { maxRedirections?: number }\n  export type DecompressInterceptorOpts = {\n    skipErrorResponses?: boolean\n    skipStatusCodes?: number[]\n  }\n\n  export type ResponseErrorInterceptorOpts = { throwOnError: boolean }\n  export type CacheInterceptorOpts = CacheHandler.CacheOptions\n\n  // DNS interceptor\n  export type DNSInterceptorRecord = { address: string, ttl: number, family: 4 | 6 }\n  export type DNSInterceptorOriginRecords = { records: { 4: { ips: DNSInterceptorRecord[] } | null, 6: { ips: DNSInterceptorRecord[] } | null } }\n  export type DNSStorage = {\n    size: number\n    get(origin: string): DNSInterceptorOriginRecords | null\n    set(origin: string, records: DNSInterceptorOriginRecords | null, options: { ttl: number }): void\n    delete(origin: string): void\n    full(): boolean\n  }\n  export type DNSInterceptorOpts = {\n    maxTTL?: number\n    maxItems?: number\n    lookup?: (origin: URL, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void\n    pick?: (origin: URL, records: DNSInterceptorOriginRecords, affinity: 4 | 6) => DNSInterceptorRecord\n    dualStack?: boolean\n    affinity?: 4 | 6\n    storage?: DNSStorage\n  }\n\n  // Deduplicate interceptor\n  export type DeduplicateMethods = 'GET' | 'HEAD' | 'OPTIONS' | 'TRACE'\n  export type DeduplicateInterceptorOpts = {\n    /**\n     * The HTTP methods to deduplicate.\n     * Note: Only safe HTTP methods can be deduplicated.\n     * @default ['GET']\n     */\n    methods?: DeduplicateMethods[]\n    /**\n     * Header names that, if present in a request, will cause the request to skip deduplication.\n     * Header name matching is case-insensitive.\n     * @default []\n     */\n    skipHeaderNames?: string[]\n    /**\n     * Header names to exclude from the deduplication key.\n     * Requests with different values for these headers will still be deduplicated together.\n     * Useful for headers like `x-request-id` that vary per request but shouldn't affect deduplication.\n     * Header name matching is case-insensitive.\n     * @default []\n     */\n    excludeHeaderNames?: string[]\n    /**\n     * Maximum bytes buffered per paused waiting deduplicated handler.\n     * If a waiting handler remains paused and exceeds this threshold,\n     * it is failed with an abort error to prevent unbounded memory growth.\n     * @default 5 * 1024 * 1024\n     */\n    maxBufferSize?: number\n  }\n\n  export function dump (opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function retry (opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function redirect (opts?: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function decompress (opts?: DecompressInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function responseError (opts?: ResponseErrorInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function dns (opts?: DNSInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function cache (opts?: CacheInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n  export function deduplicate (opts?: DeduplicateInterceptorOpts): Dispatcher.DispatcherComposeInterceptor\n}\n"
  },
  {
    "path": "types/mock-agent.d.ts",
    "content": "import Agent from './agent'\nimport Dispatcher from './dispatcher'\nimport { Interceptable, MockInterceptor } from './mock-interceptor'\nimport MockDispatch = MockInterceptor.MockDispatch\nimport { MockCallHistory } from './mock-call-history'\n\nexport default MockAgent\n\ninterface PendingInterceptor extends MockDispatch {\n  origin: string;\n}\n\n/** A mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead. */\ndeclare class MockAgent<TMockAgentOptions extends MockAgent.Options = MockAgent.Options> extends Dispatcher {\n  constructor (options?: TMockAgentOptions)\n  /** Creates and retrieves mock Dispatcher instances which can then be used to intercept HTTP requests. If the number of connections on the mock agent is set to 1, a MockClient instance is returned. Otherwise a MockPool instance is returned. */\n  get<TInterceptable extends Interceptable>(origin: string): TInterceptable\n  get<TInterceptable extends Interceptable>(origin: RegExp): TInterceptable\n  get<TInterceptable extends Interceptable>(origin: ((origin: string) => boolean)): TInterceptable\n  /** Dispatches a mocked request. */\n  dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean\n  /** Closes the mock agent and waits for registered mock pools and clients to also close before resolving. */\n  close (): Promise<void>\n  /** Disables mocking in MockAgent. */\n  deactivate (): void\n  /** Enables mocking in a MockAgent instance. When instantiated, a MockAgent is automatically activated. Therefore, this method is only effective after `MockAgent.deactivate` has been called. */\n  activate (): void\n  /** Define host matchers so only matching requests that aren't intercepted by the mock dispatchers will be attempted. */\n  enableNetConnect (): void\n  enableNetConnect (host: string): void\n  enableNetConnect (host: RegExp): void\n  enableNetConnect (host: ((host: string) => boolean)): void\n  /** Causes all requests to throw when requests are not matched in a MockAgent intercept. */\n  disableNetConnect (): void\n  /** get call history. returns the MockAgent call history or undefined if the option is not enabled. */\n  getCallHistory (): MockCallHistory | undefined\n  /** clear every call history. Any MockCallHistoryLog will be deleted on the MockCallHistory instance */\n  clearCallHistory (): void\n  /** Enable call history. Any subsequence calls will then be registered. */\n  enableCallHistory (): this\n  /** Disable call history. Any subsequence calls will then not be registered. */\n  disableCallHistory (): this\n  pendingInterceptors (): PendingInterceptor[]\n  assertNoPendingInterceptors (options?: {\n    pendingInterceptorsFormatter?: PendingInterceptorsFormatter;\n  }): void\n}\n\ninterface PendingInterceptorsFormatter {\n  format(pendingInterceptors: readonly PendingInterceptor[]): string;\n}\n\ndeclare namespace MockAgent {\n  /** MockAgent options. */\n  export interface Options extends Agent.Options {\n    /** A custom agent to be encapsulated by the MockAgent. */\n    agent?: Dispatcher;\n\n    /** Ignore trailing slashes in the path */\n    ignoreTrailingSlash?: boolean;\n\n    /** Accept URLs with search parameters using non standard syntaxes. default false */\n    acceptNonStandardSearchParameters?: boolean;\n\n    /** Enable call history. you can either call MockAgent.enableCallHistory(). default false */\n    enableCallHistory?: boolean\n  }\n}\n"
  },
  {
    "path": "types/mock-call-history.d.ts",
    "content": "import Dispatcher from './dispatcher'\n\ndeclare namespace MockCallHistoryLog {\n  /** request's configuration properties */\n  export type MockCallHistoryLogProperties = 'protocol' | 'host' | 'port' | 'origin' | 'path' | 'hash' | 'fullUrl' | 'method' | 'searchParams' | 'body' | 'headers'\n}\n\n/** a log reflecting request configuration  */\ndeclare class MockCallHistoryLog {\n  constructor (requestInit: Dispatcher.DispatchOptions)\n  /** protocol used. ie. 'https:' or 'http:' etc... */\n  protocol: string\n  /** request's host. */\n  host: string\n  /** request's port. */\n  port: string\n  /** request's origin. ie. https://localhost:3000. */\n  origin: string\n  /** path. never contains searchParams. */\n  path: string\n  /** request's hash. */\n  hash: string\n  /** the full url requested. */\n  fullUrl: string\n  /** request's method. */\n  method: string\n  /** search params. */\n  searchParams: Record<string, string>\n  /** request's body */\n  body: string | null | undefined\n  /** request's headers */\n  headers: Record<string, string | string[]> | null | undefined\n\n  /** returns an Map of property / value pair */\n  toMap (): Map<MockCallHistoryLog.MockCallHistoryLogProperties, string | Record<string, string | string[]> | null | undefined>\n\n  /** returns a string computed with all key value pair */\n  toString (): string\n}\n\ndeclare namespace MockCallHistory {\n  export type FilterCallsOperator = 'AND' | 'OR'\n\n  /** modify the filtering behavior */\n  export interface FilterCallsOptions {\n    /** the operator to apply when filtering. 'OR' will adds any MockCallHistoryLog matching any criteria given. 'AND' will adds only MockCallHistoryLog matching every criteria given. (default 'OR')  */\n    operator?: FilterCallsOperator | Lowercase<FilterCallsOperator>\n  }\n  /** a function to be executed for filtering MockCallHistoryLog */\n  export type FilterCallsFunctionCriteria = (log: MockCallHistoryLog) => boolean\n\n  /** parameter to filter MockCallHistoryLog */\n  export type FilterCallsParameter = string | RegExp | undefined | null\n\n  /** an object to execute multiple filtering at once */\n  export interface FilterCallsObjectCriteria extends Record<string, FilterCallsParameter> {\n    /** filter by request protocol. ie https: */\n    protocol?: FilterCallsParameter;\n    /** filter by request host. */\n    host?: FilterCallsParameter;\n    /** filter by request port. */\n    port?: FilterCallsParameter;\n    /** filter by request origin. */\n    origin?: FilterCallsParameter;\n    /** filter by request path. */\n    path?: FilterCallsParameter;\n    /** filter by request hash. */\n    hash?: FilterCallsParameter;\n    /** filter by request fullUrl. */\n    fullUrl?: FilterCallsParameter;\n    /** filter by request method. */\n    method?: FilterCallsParameter;\n  }\n}\n\n/** a call history to track requests configuration */\ndeclare class MockCallHistory {\n  constructor (name: string)\n  /** returns an array of MockCallHistoryLog. */\n  calls (): Array<MockCallHistoryLog>\n  /** returns the first MockCallHistoryLog */\n  firstCall (): MockCallHistoryLog | undefined\n  /** returns the last MockCallHistoryLog. */\n  lastCall (): MockCallHistoryLog | undefined\n  /** returns the nth MockCallHistoryLog. */\n  nthCall (position: number): MockCallHistoryLog | undefined\n  /** return all MockCallHistoryLog matching any of criteria given. if an object is used with multiple properties, you can change the operator to apply during filtering on options */\n  filterCalls (criteria: MockCallHistory.FilterCallsFunctionCriteria | MockCallHistory.FilterCallsObjectCriteria | RegExp, options?: MockCallHistory.FilterCallsOptions): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given protocol. if a string is given, it is matched with includes */\n  filterCallsByProtocol (protocol: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given host. if a string is given, it is matched with includes */\n  filterCallsByHost (host: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given port. if a string is given, it is matched with includes */\n  filterCallsByPort (port: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given origin. if a string is given, it is matched with includes */\n  filterCallsByOrigin (origin: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given path. if a string is given, it is matched with includes */\n  filterCallsByPath (path: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given hash. if a string is given, it is matched with includes */\n  filterCallsByHash (hash: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given fullUrl. if a string is given, it is matched with includes */\n  filterCallsByFullUrl (fullUrl: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** return all MockCallHistoryLog matching the given method. if a string is given, it is matched with includes */\n  filterCallsByMethod (method: MockCallHistory.FilterCallsParameter): Array<MockCallHistoryLog>\n  /** clear all MockCallHistoryLog on this MockCallHistory. */\n  clear (): void\n  /** use it with for..of loop or spread operator */\n  [Symbol.iterator]: () => Generator<MockCallHistoryLog>\n}\n\nexport { MockCallHistoryLog, MockCallHistory }\n"
  },
  {
    "path": "types/mock-client.d.ts",
    "content": "import Client from './client'\nimport Dispatcher from './dispatcher'\nimport MockAgent from './mock-agent'\nimport { MockInterceptor, Interceptable } from './mock-interceptor'\n\nexport default MockClient\n\n/** MockClient extends the Client API and allows one to mock requests. */\ndeclare class MockClient extends Client implements Interceptable {\n  constructor (origin: string, options: MockClient.Options)\n  /** Intercepts any matching requests that use the same origin as this mock client. */\n  intercept (options: MockInterceptor.Options): MockInterceptor\n  /** Dispatches a mocked request. */\n  dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandler): boolean\n  /** Closes the mock client and gracefully waits for enqueued requests to complete. */\n  close (): Promise<void>\n  /** Clean up all the prepared mocks. */\n  cleanMocks (): void\n}\n\ndeclare namespace MockClient {\n  /** MockClient options. */\n  export interface Options extends Client.Options {\n    /** The agent to associate this MockClient with. */\n    agent: MockAgent;\n  }\n}\n"
  },
  {
    "path": "types/mock-errors.d.ts",
    "content": "import Errors from './errors'\n\nexport default MockErrors\n\ndeclare namespace MockErrors {\n  /** The request does not match any registered mock dispatches. */\n  export class MockNotMatchedError extends Errors.UndiciError {\n    constructor (message?: string)\n    name: 'MockNotMatchedError'\n    code: 'UND_MOCK_ERR_MOCK_NOT_MATCHED'\n  }\n}\n"
  },
  {
    "path": "types/mock-interceptor.d.ts",
    "content": "import { IncomingHttpHeaders } from './header'\nimport Dispatcher from './dispatcher'\nimport { BodyInit, Headers } from './fetch'\n\n/** The scope associated with a mock dispatch. */\ndeclare class MockScope<TData extends object = object> {\n  constructor (mockDispatch: MockInterceptor.MockDispatch<TData>)\n  /** Delay a reply by a set amount of time in ms. */\n  delay (waitInMs: number): MockScope<TData>\n  /** Persist the defined mock data for the associated reply. It will return the defined mock data indefinitely. */\n  persist (): MockScope<TData>\n  /** Define a reply for a set amount of matching requests. */\n  times (repeatTimes: number): MockScope<TData>\n}\n\n/** The interceptor for a Mock. */\ndeclare class MockInterceptor {\n  constructor (options: MockInterceptor.Options, mockDispatches: MockInterceptor.MockDispatch[])\n  /** Mock an undici request with the defined reply. */\n  reply<TData extends object = object>(replyOptionsCallback: MockInterceptor.MockReplyOptionsCallback<TData>): MockScope<TData>\n  reply<TData extends object = object>(\n    statusCode: number,\n    data?: TData | Buffer | string | MockInterceptor.MockResponseDataHandler<TData>,\n    responseOptions?: MockInterceptor.MockResponseOptions\n  ): MockScope<TData>\n  /** Mock an undici request by throwing the defined reply error. */\n  replyWithError<TError extends Error = Error>(error: TError): MockScope\n  /** Set default reply headers on the interceptor for subsequent mocked replies. */\n  defaultReplyHeaders (headers: IncomingHttpHeaders): MockInterceptor\n  /** Set default reply trailers on the interceptor for subsequent mocked replies. */\n  defaultReplyTrailers (trailers: Record<string, string>): MockInterceptor\n  /** Set automatically calculated content-length header on subsequent mocked replies. */\n  replyContentLength (): MockInterceptor\n}\n\ndeclare namespace MockInterceptor {\n  /** MockInterceptor options. */\n  export interface Options {\n    /** Path to intercept on. */\n    path: string | RegExp | ((path: string) => boolean);\n    /** Method to intercept on. Defaults to GET. */\n    method?: string | RegExp | ((method: string) => boolean);\n    /** Body to intercept on. */\n    body?: string | RegExp | ((body: string) => boolean);\n    /** Headers to intercept on. */\n    headers?: Record<string, string | RegExp | ((body: string) => boolean)> | ((headers: Record<string, string>) => boolean);\n    /** Query params to intercept on */\n    query?: Record<string, any>;\n  }\n  export interface MockDispatch<TData extends object = object, TError extends Error = Error> extends Options {\n    times: number | null;\n    persist: boolean;\n    consumed: boolean;\n    data: MockDispatchData<TData, TError>;\n  }\n  export interface MockDispatchData<TData extends object = object, TError extends Error = Error> extends MockResponseOptions {\n    error: TError | null;\n    statusCode?: number;\n    data?: TData | string;\n  }\n  export interface MockResponseOptions {\n    headers?: IncomingHttpHeaders;\n    trailers?: Record<string, string>;\n  }\n\n  export interface MockResponseCallbackOptions {\n    path: string;\n    method: string;\n    headers?: Headers | Record<string, string>;\n    origin?: string;\n    body?: BodyInit | Dispatcher.DispatchOptions['body'] | null;\n  }\n\n  export type MockResponseDataHandler<TData extends object = object> = (\n    opts: MockResponseCallbackOptions\n  ) => TData | Buffer | string\n\n  export type MockReplyOptionsCallback<TData extends object = object> = (\n    opts: MockResponseCallbackOptions\n  ) => { statusCode: number, data?: TData | Buffer | string, responseOptions?: MockResponseOptions }\n}\n\ninterface Interceptable extends Dispatcher {\n  /** Intercepts any matching requests that use the same origin as this mock client. */\n  intercept(options: MockInterceptor.Options): MockInterceptor;\n  /** Clean up all the prepared mocks. */\n  cleanMocks (): void\n}\n\nexport {\n  Interceptable,\n  MockInterceptor,\n  MockScope\n}\n"
  },
  {
    "path": "types/mock-pool.d.ts",
    "content": "import Pool from './pool'\nimport MockAgent from './mock-agent'\nimport { Interceptable, MockInterceptor } from './mock-interceptor'\nimport Dispatcher from './dispatcher'\n\nexport default MockPool\n\n/** MockPool extends the Pool API and allows one to mock requests. */\ndeclare class MockPool extends Pool implements Interceptable {\n  constructor (origin: string, options: MockPool.Options)\n  /** Intercepts any matching requests that use the same origin as this mock pool. */\n  intercept (options: MockInterceptor.Options): MockInterceptor\n  /** Dispatches a mocked request. */\n  dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandler): boolean\n  /** Closes the mock pool and gracefully waits for enqueued requests to complete. */\n  close (): Promise<void>\n  /** Clean up all the prepared mocks. */\n  cleanMocks (): void\n}\n\ndeclare namespace MockPool {\n  /** MockPool options. */\n  export interface Options extends Pool.Options {\n    /** The agent to associate this MockPool with. */\n    agent: MockAgent;\n  }\n}\n"
  },
  {
    "path": "types/patch.d.ts",
    "content": "/// <reference types=\"node\" />\n\n// See https://github.com/nodejs/undici/issues/1740\n\nexport interface EventInit {\n  bubbles?: boolean\n  cancelable?: boolean\n  composed?: boolean\n}\n\nexport interface EventListenerOptions {\n  capture?: boolean\n}\n\nexport interface AddEventListenerOptions extends EventListenerOptions {\n  once?: boolean\n  passive?: boolean\n  signal?: AbortSignal\n}\n\nexport type EventListenerOrEventListenerObject = EventListener | EventListenerObject\n\nexport interface EventListenerObject {\n  handleEvent (object: Event): void\n}\n\nexport interface EventListener {\n  (evt: Event): void\n}\n"
  },
  {
    "path": "types/pool-stats.d.ts",
    "content": "import Pool from './pool'\n\nexport default PoolStats\n\ndeclare class PoolStats {\n  constructor (pool: Pool)\n  /** Number of open socket connections in this pool. */\n  connected: number\n  /** Number of open socket connections in this pool that do not have an active request. */\n  free: number\n  /** Number of pending requests across all clients in this pool. */\n  pending: number\n  /** Number of queued requests across all clients in this pool. */\n  queued: number\n  /** Number of currently active requests across all clients in this pool. */\n  running: number\n  /** Number of active, pending, or queued requests across all clients in this pool. */\n  size: number\n}\n"
  },
  {
    "path": "types/pool.d.ts",
    "content": "import Client from './client'\nimport TPoolStats from './pool-stats'\nimport { URL } from 'node:url'\nimport Dispatcher from './dispatcher'\n\nexport default Pool\n\ntype PoolConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>\n\ndeclare class Pool extends Dispatcher {\n  constructor (url: string | URL, options?: Pool.Options)\n  /** `true` after `pool.close()` has been called. */\n  closed: boolean\n  /** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */\n  destroyed: boolean\n  /** Aggregate stats for a Pool. */\n  readonly stats: TPoolStats\n\n  // Override dispatcher APIs.\n  override connect (\n    options: PoolConnectOptions\n  ): Promise<Dispatcher.ConnectData>\n  override connect (\n    options: PoolConnectOptions,\n    callback: (err: Error | null, data: Dispatcher.ConnectData) => void\n  ): void\n}\n\ndeclare namespace Pool {\n  export type PoolStats = TPoolStats\n  export interface Options extends Client.Options {\n    /** Default: `(origin, opts) => new Client(origin, opts)`. */\n    factory?(origin: URL, opts: object): Dispatcher;\n    /** The max number of clients to create. `null` if no limit. Default `null`. */\n    connections?: number | null;\n    /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */\n    clientTtl?: number | null;\n\n    interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']\n  }\n}\n"
  },
  {
    "path": "types/proxy-agent.d.ts",
    "content": "import Agent from './agent'\nimport buildConnector from './connector'\nimport Dispatcher from './dispatcher'\nimport { IncomingHttpHeaders } from './header'\n\nexport default ProxyAgent\n\ndeclare class ProxyAgent extends Dispatcher {\n  constructor (options: ProxyAgent.Options | string)\n\n  dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean\n  close (): Promise<void>\n}\n\ndeclare namespace ProxyAgent {\n  export interface Options extends Agent.Options {\n    uri: string;\n    /**\n     * @deprecated use opts.token\n     */\n    auth?: string;\n    token?: string;\n    headers?: IncomingHttpHeaders;\n    requestTls?: buildConnector.BuildOptions;\n    proxyTls?: buildConnector.BuildOptions;\n    clientFactory?(origin: URL, opts: object): Dispatcher;\n    proxyTunnel?: boolean;\n  }\n}\n"
  },
  {
    "path": "types/readable.d.ts",
    "content": "import { Readable } from 'node:stream'\nimport { Blob } from 'node:buffer'\n\nexport default BodyReadable\n\ndeclare class BodyReadable extends Readable {\n  constructor (opts: {\n    resume: (this: Readable, size: number) => void | null;\n    abort: () => void | null;\n    contentType?: string;\n    contentLength?: number;\n    highWaterMark?: number;\n  })\n\n  /** Consumes and returns the body as a string\n   *  https://fetch.spec.whatwg.org/#dom-body-text\n   */\n  text (): Promise<string>\n\n  /** Consumes and returns the body as a JavaScript Object\n   *  https://fetch.spec.whatwg.org/#dom-body-json\n   */\n  json (): Promise<unknown>\n\n  /** Consumes and returns the body as a Blob\n   *  https://fetch.spec.whatwg.org/#dom-body-blob\n   */\n  blob (): Promise<Blob>\n\n  /** Consumes and returns the body as an Uint8Array\n   *  https://fetch.spec.whatwg.org/#dom-body-bytes\n   */\n  bytes (): Promise<Uint8Array>\n\n  /** Consumes and returns the body as an ArrayBuffer\n   *  https://fetch.spec.whatwg.org/#dom-body-arraybuffer\n   */\n  arrayBuffer (): Promise<ArrayBuffer>\n\n  /** Not implemented\n   *\n   *  https://fetch.spec.whatwg.org/#dom-body-formdata\n   */\n  formData (): Promise<never>\n\n  /** Returns true if the body is not null and the body has been consumed\n   *\n   *  Otherwise, returns false\n   *\n   * https://fetch.spec.whatwg.org/#dom-body-bodyused\n   */\n  readonly bodyUsed: boolean\n\n  /**\n   * If body is null, it should return null as the body\n   *\n   *  If body is not null, should return the body as a ReadableStream\n   *\n   *  https://fetch.spec.whatwg.org/#dom-body-body\n   */\n  readonly body: never | undefined\n\n  /** Dumps the response body by reading `limit` number of bytes.\n   * @param opts.limit Number of bytes to read (optional) - Default: 131072\n   * @param opts.signal AbortSignal to cancel the operation (optional)\n   */\n  dump (opts?: { limit: number; signal?: AbortSignal }): Promise<void>\n}\n"
  },
  {
    "path": "types/retry-agent.d.ts",
    "content": "import Dispatcher from './dispatcher'\nimport RetryHandler from './retry-handler'\n\nexport default RetryAgent\n\ndeclare class RetryAgent extends Dispatcher {\n  constructor (dispatcher: Dispatcher, options?: RetryHandler.RetryOptions)\n}\n"
  },
  {
    "path": "types/retry-handler.d.ts",
    "content": "import Dispatcher from './dispatcher'\n\nexport default RetryHandler\n\ndeclare class RetryHandler implements Dispatcher.DispatchHandler {\n  constructor (\n    options: Dispatcher.DispatchOptions & {\n      retryOptions?: RetryHandler.RetryOptions;\n    },\n    retryHandlers: RetryHandler.RetryHandlers\n  )\n}\n\ndeclare namespace RetryHandler {\n  export type RetryState = { counter: number; }\n\n  export type RetryContext = {\n    state: RetryState;\n    opts: Dispatcher.DispatchOptions & {\n      retryOptions?: RetryHandler.RetryOptions;\n    };\n  }\n\n  export type OnRetryCallback = (result?: Error | null) => void\n\n  export type RetryCallback = (\n    err: Error,\n    context: {\n      state: RetryState;\n      opts: Dispatcher.DispatchOptions & {\n        retryOptions?: RetryHandler.RetryOptions;\n      };\n    },\n    callback: OnRetryCallback\n  ) => void\n\n  export interface RetryOptions {\n    /**\n     * If true, the retry handler will throw an error if the request fails,\n     * this will prevent the folling handlers from being called, and will destroy the socket.\n     *\n     * @type {boolean}\n     * @memberof RetryOptions\n     * @default true\n     */\n    throwOnError?: boolean;\n    /**\n     * Callback to be invoked on every retry iteration.\n     * It receives the error, current state of the retry object and the options object\n     * passed when instantiating the retry handler.\n     *\n     * @type {RetryCallback}\n     * @memberof RetryOptions\n     */\n    retry?: RetryCallback;\n    /**\n     * Maximum number of retries to allow.\n     *\n     * @type {number}\n     * @memberof RetryOptions\n     * @default 5\n     */\n    maxRetries?: number;\n    /**\n     * Max number of milliseconds allow between retries\n     *\n     * @type {number}\n     * @memberof RetryOptions\n     * @default 30000\n     */\n    maxTimeout?: number;\n    /**\n     * Initial number of milliseconds to wait before retrying for the first time.\n     *\n     * @type {number}\n     * @memberof RetryOptions\n     * @default 500\n     */\n    minTimeout?: number;\n    /**\n     * Factior to multiply the timeout factor between retries.\n     *\n     * @type {number}\n     * @memberof RetryOptions\n     * @default 2\n     */\n    timeoutFactor?: number;\n    /**\n     * It enables to automatically infer timeout between retries based on the `Retry-After` header.\n     *\n     * @type {boolean}\n     * @memberof RetryOptions\n     * @default true\n     */\n    retryAfter?: boolean;\n    /**\n     * HTTP methods to retry.\n     *\n     * @type {Dispatcher.HttpMethod[]}\n     * @memberof RetryOptions\n     * @default ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],\n     */\n    methods?: Dispatcher.HttpMethod[];\n    /**\n     * Error codes to be retried. e.g. `ECONNRESET`, `ENOTFOUND`, `ETIMEDOUT`, `ECONNREFUSED`, etc.\n     *\n     * @type {string[]}\n     * @default ['ECONNRESET','ECONNREFUSED','ENOTFOUND','ENETDOWN','ENETUNREACH','EHOSTDOWN','EHOSTUNREACH','EPIPE']\n     */\n    errorCodes?: string[];\n    /**\n     * HTTP status codes to be retried.\n     *\n     * @type {number[]}\n     * @memberof RetryOptions\n     * @default [500, 502, 503, 504, 429],\n     */\n    statusCodes?: number[];\n  }\n\n  export interface RetryHandlers {\n    dispatch: Dispatcher['dispatch'];\n    handler: Dispatcher.DispatchHandler;\n  }\n}\n"
  },
  {
    "path": "types/round-robin-pool.d.ts",
    "content": "import Client from './client'\nimport TPoolStats from './pool-stats'\nimport { URL } from 'node:url'\nimport Dispatcher from './dispatcher'\n\nexport default RoundRobinPool\n\ntype RoundRobinPoolConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>\n\ndeclare class RoundRobinPool extends Dispatcher {\n  constructor (url: string | URL, options?: RoundRobinPool.Options)\n  /** `true` after `pool.close()` has been called. */\n  closed: boolean\n  /** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */\n  destroyed: boolean\n  /** Aggregate stats for a RoundRobinPool. */\n  readonly stats: TPoolStats\n\n  // Override dispatcher APIs.\n  override connect (\n    options: RoundRobinPoolConnectOptions\n  ): Promise<Dispatcher.ConnectData>\n  override connect (\n    options: RoundRobinPoolConnectOptions,\n    callback: (err: Error | null, data: Dispatcher.ConnectData) => void\n  ): void\n}\n\ndeclare namespace RoundRobinPool {\n  export type RoundRobinPoolStats = TPoolStats\n  export interface Options extends Client.Options {\n    /** Default: `(origin, opts) => new Client(origin, opts)`. */\n    factory?(origin: URL, opts: object): Dispatcher;\n    /** The max number of clients to create. `null` if no limit. Default `null`. */\n    connections?: number | null;\n    /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */\n    clientTtl?: number | null;\n\n    interceptors?: { RoundRobinPool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']\n  }\n}\n"
  },
  {
    "path": "types/snapshot-agent.d.ts",
    "content": "import MockAgent from './mock-agent'\n\ndeclare class SnapshotRecorder {\n  constructor (options?: SnapshotRecorder.Options)\n\n  record (requestOpts: any, response: any): Promise<void>\n  findSnapshot (requestOpts: any): SnapshotRecorder.Snapshot | undefined\n  loadSnapshots (filePath?: string): Promise<void>\n  saveSnapshots (filePath?: string): Promise<void>\n  clear (): void\n  getSnapshots (): SnapshotRecorder.Snapshot[]\n  size (): number\n  resetCallCounts (): void\n  deleteSnapshot (requestOpts: any): boolean\n  getSnapshotInfo (requestOpts: any): SnapshotRecorder.SnapshotInfo | null\n  replaceSnapshots (snapshotData: SnapshotRecorder.SnapshotData[]): void\n  destroy (): void\n}\n\ndeclare namespace SnapshotRecorder {\n  type SnapshotRecorderMode = 'record' | 'playback' | 'update'\n\n  export interface Options {\n    snapshotPath?: string\n    mode?: SnapshotRecorderMode\n    maxSnapshots?: number\n    autoFlush?: boolean\n    flushInterval?: number\n    matchHeaders?: string[]\n    ignoreHeaders?: string[]\n    excludeHeaders?: string[]\n    matchBody?: boolean\n    matchQuery?: boolean\n    caseSensitive?: boolean\n    shouldRecord?: (requestOpts: any) => boolean\n    shouldPlayback?: (requestOpts: any) => boolean\n    excludeUrls?: (string | RegExp)[]\n  }\n\n  export interface Snapshot {\n    request: {\n      method: string\n      url: string\n      headers: Record<string, string>\n      body?: string\n    }\n    responses: {\n      statusCode: number\n      headers: Record<string, string>\n      body: string\n      trailers: Record<string, string>\n    }[]\n    callCount: number\n    timestamp: string\n  }\n\n  export interface SnapshotInfo {\n    hash: string\n    request: {\n      method: string\n      url: string\n      headers: Record<string, string>\n      body?: string\n    }\n    responseCount: number\n    callCount: number\n    timestamp: string\n  }\n\n  export interface SnapshotData {\n    hash: string\n    snapshot: Snapshot\n  }\n}\n\ndeclare class SnapshotAgent extends MockAgent {\n  constructor (options?: SnapshotAgent.Options)\n\n  saveSnapshots (filePath?: string): Promise<void>\n  loadSnapshots (filePath?: string): Promise<void>\n  getRecorder (): SnapshotRecorder\n  getMode (): SnapshotRecorder.SnapshotRecorderMode\n  clearSnapshots (): void\n  resetCallCounts (): void\n  deleteSnapshot (requestOpts: any): boolean\n  getSnapshotInfo (requestOpts: any): SnapshotRecorder.SnapshotInfo | null\n  replaceSnapshots (snapshotData: SnapshotRecorder.SnapshotData[]): void\n}\n\ndeclare namespace SnapshotAgent {\n  export interface Options extends MockAgent.Options {\n    mode?: SnapshotRecorder.SnapshotRecorderMode\n    snapshotPath?: string\n    maxSnapshots?: number\n    autoFlush?: boolean\n    flushInterval?: number\n    matchHeaders?: string[]\n    ignoreHeaders?: string[]\n    excludeHeaders?: string[]\n    matchBody?: boolean\n    matchQuery?: boolean\n    caseSensitive?: boolean\n    shouldRecord?: (requestOpts: any) => boolean\n    shouldPlayback?: (requestOpts: any) => boolean\n    excludeUrls?: (string | RegExp)[]\n  }\n}\n\nexport { SnapshotAgent, SnapshotRecorder }\n"
  },
  {
    "path": "types/socks5-proxy-agent.d.ts",
    "content": "import Dispatcher from './dispatcher'\nimport buildConnector from './connector'\nimport { IncomingHttpHeaders } from './header'\nimport Pool from './pool'\n\nexport default Socks5ProxyAgent\n\ndeclare class Socks5ProxyAgent extends Dispatcher {\n  constructor (proxyUrl: string | URL, options?: Socks5ProxyAgent.Options)\n}\n\ndeclare namespace Socks5ProxyAgent {\n  export interface Options extends Pool.Options {\n    /** Additional headers to send with the proxy connection */\n    headers?: IncomingHttpHeaders;\n    /** SOCKS5 proxy username for authentication */\n    username?: string;\n    /** SOCKS5 proxy password for authentication */\n    password?: string;\n    /** Custom connector function for proxy connection */\n    connect?: buildConnector.connector;\n    /** TLS options for the proxy connection (for SOCKS5 over TLS) */\n    proxyTls?: buildConnector.BuildOptions;\n  }\n}\n"
  },
  {
    "path": "types/util.d.ts",
    "content": "export namespace util {\n  /**\n   * Retrieves a header name and returns its lowercase value.\n   * @param value Header name\n   */\n  export function headerNameToString (value: string | Buffer): string\n\n  /**\n   * Receives a header object and returns the parsed value.\n   * @param headers Header object\n   * @param obj Object to specify a proxy object. Used to assign parsed values.\n   * @returns If `obj` is specified, it is equivalent to `obj`.\n   */\n  export function parseHeaders (\n    headers: (Buffer | string | (Buffer | string)[])[],\n    obj?: Record<string, string | string[]>\n  ): Record<string, string | string[]>\n}\n"
  },
  {
    "path": "types/utility.d.ts",
    "content": "type AutocompletePrimitiveBaseType<T> =\n  T extends string ? string :\n    T extends number ? number :\n      T extends boolean ? boolean :\n        never\n\nexport type Autocomplete<T> = T | (AutocompletePrimitiveBaseType<T> & Record<never, never>)\n"
  },
  {
    "path": "types/webidl.d.ts",
    "content": "// These types are not exported, and are only used internally\nimport { BufferSource } from 'node:stream/web'\nimport * as undici from './index'\n\n/**\n * Take in an unknown value and return one that is of type T\n */\ntype Converter<T> = (object: unknown) => T\n\ntype SequenceConverter<T> = (object: unknown, iterable?: IterableIterator<T>) => T[]\n\ntype RecordConverter<K extends string, V> = (object: unknown) => Record<K, V>\n\ninterface WebidlErrors {\n  /**\n   * @description Instantiate an error\n   */\n  exception (opts: { header: string, message: string }): TypeError\n  /**\n   * @description Instantiate an error when conversion from one type to another has failed\n   */\n  conversionFailed (opts: {\n    prefix: string\n    argument: string\n    types: string[]\n  }): TypeError\n  /**\n   * @description Throw an error when an invalid argument is provided\n   */\n  invalidArgument (opts: {\n    prefix: string\n    value: string\n    type: string\n  }): TypeError\n}\n\ninterface WebIDLTypes {\n  UNDEFINED: 1,\n  BOOLEAN: 2,\n  STRING: 3,\n  SYMBOL: 4,\n  NUMBER: 5,\n  BIGINT: 6,\n  NULL: 7\n  OBJECT: 8\n}\n\ninterface WebidlUtil {\n  /**\n   * @see https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values\n   */\n  Type (object: unknown): WebIDLTypes[keyof WebIDLTypes]\n\n  TypeValueToString (o: unknown):\n    | 'Undefined'\n    | 'Boolean'\n    | 'String'\n    | 'Symbol'\n    | 'Number'\n    | 'BigInt'\n    | 'Null'\n    | 'Object'\n\n  Types: WebIDLTypes\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#abstract-opdef-converttoint\n   */\n  ConvertToInt (\n    V: unknown,\n    bitLength: number,\n    signedness: 'signed' | 'unsigned',\n    flags?: number\n  ): number\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#abstract-opdef-integerpart\n   */\n  IntegerPart (N: number): number\n\n  /**\n   * Stringifies {@param V}\n   */\n  Stringify (V: any): string\n\n  MakeTypeAssertion <I>(I: I): (arg: any) => arg is I\n\n  /**\n   * Mark a value as uncloneable for Node.js.\n   * This is only effective in some newer Node.js versions.\n   */\n  markAsUncloneable (V: any): void\n\n  IsResizableArrayBuffer (V: ArrayBufferLike): boolean\n\n  HasFlag (flag: number, attributes: number): boolean\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy\n   */\n  getCopyOfBytesHeldByBufferSource (bufferSource: BufferSource): Uint8Array\n}\n\ninterface WebidlConverters {\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-DOMString\n   */\n  DOMString (V: unknown, prefix: string, argument: string, flags?: number): string\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-ByteString\n   */\n  ByteString (V: unknown, prefix: string, argument: string): string\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-USVString\n   */\n  USVString (V: unknown): string\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-boolean\n   */\n  boolean (V: unknown): boolean\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-any\n   */\n  any <Value>(V: Value): Value\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-long-long\n   */\n  ['long long'] (V: unknown): number\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-unsigned-long-long\n   */\n  ['unsigned long long'] (V: unknown): number\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-unsigned-long\n   */\n  ['unsigned long'] (V: unknown): number\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-unsigned-short\n   */\n  ['unsigned short'] (V: unknown, flags?: number): number\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#idl-ArrayBuffer\n   */\n  ArrayBuffer (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    options?: { allowResizable: boolean }\n  ): ArrayBuffer\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#idl-SharedArrayBuffer\n   */\n  SharedArrayBuffer (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    options?: { allowResizable: boolean }\n  ): SharedArrayBuffer\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-buffer-source-types\n   */\n  TypedArray (\n    V: unknown,\n    T: new () => NodeJS.TypedArray,\n    prefix: string,\n    argument: string,\n    flags?: number\n  ): NodeJS.TypedArray\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-buffer-source-types\n   */\n  DataView (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    flags?: number\n  ): DataView\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-buffer-source-types\n   */\n  ArrayBufferView (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    flags?: number\n  ): NodeJS.ArrayBufferView\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#BufferSource\n   */\n  BufferSource (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    flags?: number\n  ): ArrayBuffer | NodeJS.ArrayBufferView\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#AllowSharedBufferSource\n   */\n  AllowSharedBufferSource (\n    V: unknown,\n    prefix: string,\n    argument: string,\n    flags?: number\n  ): ArrayBuffer | SharedArrayBuffer | NodeJS.ArrayBufferView\n\n  ['sequence<ByteString>']: SequenceConverter<string>\n\n  ['sequence<sequence<ByteString>>']: SequenceConverter<string[]>\n\n  ['record<ByteString, ByteString>']: RecordConverter<string, string>\n\n  /**\n  * @see https://fetch.spec.whatwg.org/#requestinfo\n  */\n  RequestInfo (V: unknown): undici.Request | string\n\n  /**\n   * @see https://fetch.spec.whatwg.org/#requestinit\n   */\n  RequestInit (V: unknown): undici.RequestInit\n\n  /**\n   * @see https://html.spec.whatwg.org/multipage/webappapis.html#eventhandlernonnull\n   */\n  EventHandlerNonNull (V: unknown): Function | null\n\n  WebSocketStreamWrite (V: unknown): ArrayBuffer | NodeJS.TypedArray | string\n\n  [Key: string]: (...args: any[]) => unknown\n}\n\ntype WebidlIsFunction<T> = (arg: any) => arg is T\n\ninterface WebidlIs {\n  Request: WebidlIsFunction<undici.Request>\n  Response: WebidlIsFunction<undici.Response>\n  ReadableStream: WebidlIsFunction<ReadableStream>\n  Blob: WebidlIsFunction<Blob>\n  URLSearchParams: WebidlIsFunction<URLSearchParams>\n  File: WebidlIsFunction<File>\n  FormData: WebidlIsFunction<undici.FormData>\n  URL: WebidlIsFunction<URL>\n  WebSocketError: WebidlIsFunction<undici.WebSocketError>\n  AbortSignal: WebidlIsFunction<AbortSignal>\n  MessagePort: WebidlIsFunction<MessagePort>\n  USVString: WebidlIsFunction<string>\n  /**\n   * @see https://webidl.spec.whatwg.org/#BufferSource\n   */\n  BufferSource: WebidlIsFunction<ArrayBuffer | NodeJS.TypedArray>\n}\n\nexport interface Webidl {\n  errors: WebidlErrors\n  util: WebidlUtil\n  converters: WebidlConverters\n  is: WebidlIs\n  attributes: WebIDLExtendedAttributes\n\n  /**\n   * @description Performs a brand-check on {@param V} to ensure it is a\n   * {@param cls} object.\n   */\n  brandCheck <Interface extends new () => unknown>(V: unknown, cls: Interface): asserts V is Interface\n\n  brandCheckMultiple <Interfaces extends (new () => unknown)[]> (list: Interfaces): (V: any) => asserts V is Interfaces[number]\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-sequence\n   * @description Convert a value, V, to a WebIDL sequence type.\n   */\n  sequenceConverter <Type>(C: Converter<Type>): SequenceConverter<Type>\n\n  illegalConstructor (): never\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#es-to-record\n   * @description Convert a value, V, to a WebIDL record type.\n   */\n  recordConverter <K extends string, V>(\n    keyConverter: Converter<K>,\n    valueConverter: Converter<V>\n  ): RecordConverter<K, V>\n\n  /**\n   * Similar to {@link Webidl.brandCheck} but allows skipping the check if third party\n   * interfaces are allowed.\n   */\n  interfaceConverter <Interface>(typeCheck: WebidlIsFunction<Interface>, name: string): (\n    V: unknown,\n    prefix: string,\n    argument: string\n  ) => asserts V is Interface\n\n  // TODO(@KhafraDev): a type could likely be implemented that can infer the return type\n  // from the converters given?\n  /**\n   * Converts a value, V, to a WebIDL dictionary types. Allows limiting which keys are\n   * allowed, values allowed, optional and required keys. Auto converts the value to\n   * a type given a converter.\n   */\n  dictionaryConverter (converters: {\n    key: string,\n    defaultValue?: () => unknown,\n    required?: boolean,\n    converter: (...args: unknown[]) => unknown,\n    allowedValues?: unknown[]\n  }[]): (V: unknown) => Record<string, unknown>\n\n  /**\n   * @see https://webidl.spec.whatwg.org/#idl-nullable-type\n   * @description allows a type, V, to be null\n   */\n  nullableConverter <T>(\n    converter: Converter<T>\n  ): (V: unknown) => ReturnType<typeof converter> | null\n\n  argumentLengthCheck (args: { length: number }, min: number, context: string): void\n}\n\ninterface WebIDLExtendedAttributes {\n  /** https://webidl.spec.whatwg.org/#Clamp */\n  Clamp: number\n  /** https://webidl.spec.whatwg.org/#EnforceRange */\n  EnforceRange: number\n  /** https://webidl.spec.whatwg.org/#AllowShared */\n  AllowShared: number\n  /** https://webidl.spec.whatwg.org/#AllowResizable */\n  AllowResizable: number\n  /** https://webidl.spec.whatwg.org/#LegacyNullToEmptyString */\n  LegacyNullToEmptyString: number\n}\n"
  },
  {
    "path": "types/websocket.d.ts",
    "content": "/// <reference types=\"node\" />\n\nimport type { Blob } from 'node:buffer'\nimport type { ReadableStream, WritableStream } from 'node:stream/web'\nimport type { MessagePort } from 'node:worker_threads'\nimport {\n  EventInit,\n  EventListenerOptions,\n  AddEventListenerOptions,\n  EventListenerOrEventListenerObject\n} from './patch'\nimport Dispatcher from './dispatcher'\nimport { HeadersInit } from './fetch'\n\nexport type BinaryType = 'blob' | 'arraybuffer'\n\ninterface WebSocketEventMap {\n  close: CloseEvent\n  error: ErrorEvent\n  message: MessageEvent\n  open: Event\n}\n\ninterface WebSocket extends EventTarget {\n  binaryType: BinaryType\n\n  readonly bufferedAmount: number\n  readonly extensions: string\n\n  onclose: ((this: WebSocket, ev: WebSocketEventMap['close']) => any) | null\n  onerror: ((this: WebSocket, ev: WebSocketEventMap['error']) => any) | null\n  onmessage: ((this: WebSocket, ev: WebSocketEventMap['message']) => any) | null\n  onopen: ((this: WebSocket, ev: WebSocketEventMap['open']) => any) | null\n\n  readonly protocol: string\n  readonly readyState: number\n  readonly url: string\n\n  close(code?: number, reason?: string): void\n  send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void\n\n  readonly CLOSED: number\n  readonly CLOSING: number\n  readonly CONNECTING: number\n  readonly OPEN: number\n\n  addEventListener<K extends keyof WebSocketEventMap>(\n    type: K,\n    listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,\n    options?: boolean | AddEventListenerOptions\n  ): void\n  addEventListener(\n    type: string,\n    listener: EventListenerOrEventListenerObject,\n    options?: boolean | AddEventListenerOptions\n  ): void\n  removeEventListener<K extends keyof WebSocketEventMap>(\n    type: K,\n    listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,\n    options?: boolean | EventListenerOptions\n  ): void\n  removeEventListener(\n    type: string,\n    listener: EventListenerOrEventListenerObject,\n    options?: boolean | EventListenerOptions\n  ): void\n}\n\nexport declare const WebSocket: {\n  prototype: WebSocket\n  new (url: string | URL, protocols?: string | string[] | WebSocketInit): WebSocket\n  readonly CLOSED: number\n  readonly CLOSING: number\n  readonly CONNECTING: number\n  readonly OPEN: number\n}\n\ninterface CloseEventInit extends EventInit {\n  code?: number\n  reason?: string\n  wasClean?: boolean\n}\n\ninterface CloseEvent extends Event {\n  readonly code: number\n  readonly reason: string\n  readonly wasClean: boolean\n}\n\nexport declare const CloseEvent: {\n  prototype: CloseEvent\n  new (type: string, eventInitDict?: CloseEventInit): CloseEvent\n}\n\ninterface MessageEventInit<T = any> extends EventInit {\n  data?: T\n  lastEventId?: string\n  origin?: string\n  ports?: MessagePort[]\n  source?: MessagePort | null\n}\n\ninterface MessageEvent<T = any> extends Event {\n  readonly data: T\n  readonly lastEventId: string\n  readonly origin: string\n  readonly ports: readonly MessagePort[]\n  readonly source: MessagePort | null\n  initMessageEvent(\n    type: string,\n    bubbles?: boolean,\n    cancelable?: boolean,\n    data?: any,\n    origin?: string,\n    lastEventId?: string,\n    source?: MessagePort | null,\n    ports?: MessagePort[]\n  ): void;\n}\n\nexport declare const MessageEvent: {\n  prototype: MessageEvent\n  new<T>(type: string, eventInitDict?: MessageEventInit<T>): MessageEvent<T>\n}\n\ninterface ErrorEventInit extends EventInit {\n  message?: string\n  filename?: string\n  lineno?: number\n  colno?: number\n  error?: any\n}\n\ninterface ErrorEvent extends Event {\n  readonly message: string\n  readonly filename: string\n  readonly lineno: number\n  readonly colno: number\n  readonly error: Error\n}\n\nexport declare const ErrorEvent: {\n  prototype: ErrorEvent\n  new (type: string, eventInitDict?: ErrorEventInit): ErrorEvent\n}\n\ninterface WebSocketInit {\n  protocols?: string | string[],\n  dispatcher?: Dispatcher,\n  headers?: HeadersInit\n}\n\ninterface WebSocketStreamOptions {\n  protocols?: string | string[]\n  signal?: AbortSignal\n}\n\ninterface WebSocketCloseInfo {\n  closeCode: number\n  reason: string\n}\n\ninterface WebSocketStream {\n  closed: Promise<WebSocketCloseInfo>\n  opened: Promise<{\n    extensions: string\n    protocol: string\n    readable: ReadableStream\n    writable: WritableStream\n  }>\n  url: string\n\n  close(options?: Partial<WebSocketCloseInfo>): void\n}\n\nexport declare const WebSocketStream: {\n  prototype: WebSocketStream\n  new (url: string | URL, options?: WebSocketStreamOptions): WebSocketStream\n}\n\ninterface WebSocketError extends Event, WebSocketCloseInfo {}\n\nexport declare const WebSocketError: {\n  prototype: WebSocketError\n  new (type: string, init?: WebSocketCloseInfo): WebSocketError\n}\n\nexport declare const ping: (ws: WebSocket, body?: Buffer) => void\n"
  }
]