Full Code of JakeChampion/fetch for AI

main ba5cf1ed2e02 cached
28 files
140.2 KB
37.1k tokens
26 symbols
1 requests
Download .txt
Repository: JakeChampion/fetch
Branch: main
Commit: ba5cf1ed2e02
Files: 28
Total size: 140.2 KB

Directory structure:
gitextract_sunlppts/

├── .eslintignore
├── .eslintrc.js
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── lock.yml
│       ├── node.js.yml
│       ├── release.yml
│       └── scorecard.yml
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── bower.json
├── fetch.js
├── fetch.js.flow
├── package.json
├── prettier.config.js
├── rollup.config.js
└── test/
    ├── karma-worker.config.js
    ├── karma.config.js
    ├── server.js
    ├── test.js
    ├── worker-adapter.js
    └── worker.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
node_modules


================================================
FILE: .eslintrc.js
================================================
/* eslint-env node */
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "rules": {
    }
};


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily
    groups:
      github-actions:
        patterns:
          - "*"
    open-pull-requests-limit: 1


================================================
FILE: .github/workflows/lock.yml
================================================
name: 'Lock threads'

on:
  schedule:
    - cron: '0 0 * * *'

permissions: {}

jobs:
  lock:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
        with:
          github-token: ${{ github.token }}
          issue-lock-inactive-days: '180'


================================================
FILE: .github/workflows/node.js.yml
================================================
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
    
permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm i
    - run: npm run build --if-present
    - run: npm test


================================================
FILE: .github/workflows/release.yml
================================================
on:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write

name: release-please

jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: google-github-actions/release-please-action@a37ac6e4f6449ce8b3f7607e4d97d0146028dc0b # v4.1.0
        with:
          release-type: node
          package-name: release-please-action


================================================
FILE: .github/workflows/scorecard.yml
================================================
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.

name: Scorecard supply-chain security
on:
  # For Branch-Protection check. Only the default branch is supported. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
  branch_protection_rule:
  # To guarantee Maintained check is occasionally updated. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
  schedule:
    - cron: '17 14 * * 1'
  push:
    branches: [ main ]

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecard analysis
    runs-on: ubuntu-latest
    permissions:
      security-events: write # to upload the results to code-scanning dashboard
      id-token: write # to publish results and get a badge (see publish_results below)

    steps:
      - name: "Checkout code"
        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.1.0
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
        with:
          results_file: results.sarif
          results_format: sarif
          #  Publish results to OpenSSF REST API for easy access by consumers and
          #  allows the repository to include the Scorecard badge.
          #  See https://github.com/ossf/scorecard-action#publishing-results.
          publish_results: true

      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
      # format to the repository Actions tab.
      - name: "Upload artifact"
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
        with:
          sarif_file: results.sarif


================================================
FILE: .gitignore
================================================
.env
package-lock.json
dist/
bower_components/
node_modules/
sauce_connect/
sauce_connect.log


================================================
FILE: .npmignore
================================================
.env
package-lock.json
bower_components/
node_modules/
sauce_connect/
sauce_connect.log


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file. Dates are displayed in UTC.

## [3.6.20](https://github.com/JakeChampion/fetch/compare/v3.6.19...v3.6.20) (2023-12-13)


### Bug Fixes

* Response.error().ok === false ([#1412](https://github.com/JakeChampion/fetch/issues/1412)) ([27e1c75](https://github.com/JakeChampion/fetch/commit/27e1c75f830f0b70a40b511e03652776951aca75))

## [3.6.19](https://github.com/JakeChampion/fetch/compare/v3.6.18...v3.6.19) (2023-09-11)


### Bug Fixes

* Have unique error messages for xhr timeouts and errors ([#1380](https://github.com/JakeChampion/fetch/issues/1380)) ([7170f0b](https://github.com/JakeChampion/fetch/commit/7170f0b127d16c5895aba61c9168482834809046))


## [v3.6.18](https://github.com/JakeChampion/fetch/compare/v3.6.17...v3.6.18)

- Fix - File fetching broken since commit 0c1d2b9 [`#1375`](https://github.com/JakeChampion/fetch/pull/1375)
- Remove broken links [`1dc07c6`](https://github.com/JakeChampion/fetch/commit/1dc07c6064a32e989306fb2324204c56c93140fe)
- automatically generate a changelog [`0e7d1dd`](https://github.com/JakeChampion/fetch/commit/0e7d1dd95826b3b76510f0832784207f2609145e)

## [v3.6.17](https://github.com/JakeChampion/fetch/compare/v3.6.16...v3.6.17)

> 20 July 2023

- Revert "Resolves https://github.com/JakeChampion/fetch/issues/928" [`#928`](https://github.com/JakeChampion/fetch/issues/928)

## [v3.6.16](https://github.com/JakeChampion/fetch/compare/v3.6.15...v3.6.16)

> 18 July 2023

- Resolves https://github.com/JakeChampion/fetch/issues/928 [`#928`](https://github.com/JakeChampion/fetch/issues/928)

## [v3.6.15](https://github.com/JakeChampion/fetch/compare/v3.6.14...v3.6.15)

> 18 July 2023

- fix https://github.com/JakeChampion/fetch/issues/997 [`#997`](https://github.com/JakeChampion/fetch/issues/997)

## [v3.6.14](https://github.com/JakeChampion/fetch/compare/v3.6.13...v3.6.14)

> 18 July 2023

- Fix https://github.com/JakeChampion/fetch/issues/1076 [`#1076`](https://github.com/JakeChampion/fetch/issues/1076)

## [v3.6.13](https://github.com/JakeChampion/fetch/compare/v3.6.12...v3.6.13)

> 18 July 2023

- respect charset within readBlobAsText [`#1059`](https://github.com/JakeChampion/fetch/issues/1059)

## [v3.6.12](https://github.com/JakeChampion/fetch/compare/v3.6.11...v3.6.12)

> 18 July 2023

- fix: Headers only accepts array which have nested array of length 2 [`#1235`](https://github.com/JakeChampion/fetch/issues/1235)

## [v3.6.11](https://github.com/JakeChampion/fetch/compare/v3.6.10...v3.6.11)

> 18 July 2023

- Define Body.arrayBuffer even if support.blob is false [`#992`](https://github.com/JakeChampion/fetch/issues/992)

## [v3.6.10](https://github.com/JakeChampion/fetch/compare/v3.6.9...v3.6.10)

> 18 July 2023

- use globals if they exist [`dffc542`](https://github.com/JakeChampion/fetch/commit/dffc542fe7140f35ee7fec29e3da67f3bf080910)

## [v3.6.9](https://github.com/JakeChampion/fetch/compare/v3.6.8...v3.6.9)

> 18 July 2023

- fix: when no body supplied, do not set bodyUsed to true [`7d92dff`](https://github.com/JakeChampion/fetch/commit/7d92dff12d7c4058b57c7e77adeb0a76ffab639f)

## [v3.6.8](https://github.com/JakeChampion/fetch/compare/v3.6.7...v3.6.8)

> 18 July 2023

- validate status is in range [`#1213`](https://github.com/JakeChampion/fetch/issues/1213)

## [v3.6.7](https://github.com/JakeChampion/fetch/compare/v3.6.6...v3.6.7)

> 18 July 2023

- dont shadow `global` [`#1026`](https://github.com/JakeChampion/fetch/issues/1026)
- dont use github  eslint [`408d3b6`](https://github.com/JakeChampion/fetch/commit/408d3b60e27abef325dd898d899430c46a0012b2)
- remove invalid-headers test [`e3f6590`](https://github.com/JakeChampion/fetch/commit/e3f65907924b7692af7c08cd92044456bc92ad8b)
- Update lock.yml permissions [`e97321b`](https://github.com/JakeChampion/fetch/commit/e97321bc081e80275397fc4c7a990791aa8b3524)

## [v3.6.6](https://github.com/JakeChampion/fetch/compare/v3.6.5...v3.6.6)

> 18 July 2023

- fix: ignore not throw on invalid response headers [`#930`](https://github.com/JakeChampion/fetch/issues/930)

## [v3.6.5](https://github.com/JakeChampion/fetch/compare/v3.6.4...v3.6.5)

> 18 July 2023

- Add some missed methods which should be normalized as uppercase [`a43b628`](https://github.com/JakeChampion/fetch/commit/a43b6283833c403230bb1a5238e2d7ac435c52da)
- Update caniuse link to use HTTPS and new pattern [`fb5b0cf`](https://github.com/JakeChampion/fetch/commit/fb5b0cf42b470faf8c5448ab461d561f34380a30)

## [v3.6.4](https://github.com/JakeChampion/fetch/compare/v3.6.3...v3.6.4)

> 18 July 2023

- always set a signal on Request [`d1d09fb`](https://github.com/JakeChampion/fetch/commit/d1d09fb8039b4b8c7f2f5d6c844ea72d8a3cefe6)

## [v3.6.3](https://github.com/JakeChampion/fetch/compare/v3.6.2...v3.6.3)

> 18 July 2023

- Compatible global equals to the false [`7727e50`](https://github.com/JakeChampion/fetch/commit/7727e50493eafae9a7005f10f18f81e5bbcbfdd3)

## [v3.6.2](https://github.com/JakeChampion/fetch/compare/v3.6.1...v3.6.2)

> 27 February 2021

- Revert "Represent non-stringified JSON request body as an [object Object] string" [`e42f201`](https://github.com/JakeChampion/fetch/commit/e42f201b8b0af8b3f2615abe8161c8087f52f1b2)

## [v3.6.1](https://github.com/JakeChampion/fetch/compare/v3.6.0...v3.6.1)

> 18 February 2021

- Fix MSIE compatibility [`da97bdb`](https://github.com/JakeChampion/fetch/commit/da97bdb462632288b21eeca67fc6b93c7077ebae)
- use var instead of const [`5d3952d`](https://github.com/JakeChampion/fetch/commit/5d3952d10736a98a550043b933c50800643e2756)
- Restore package.json [`6b4bd97`](https://github.com/JakeChampion/fetch/commit/6b4bd971b1e415a347cf20db4b925d1b845669a9)

## [v3.6.0](https://github.com/JakeChampion/fetch/compare/v3.5.0...v3.6.0)

> 18 February 2021

- Fix statusText: undefined should give '' and null should give 'null' [`b5c8bd0`](https://github.com/JakeChampion/fetch/commit/b5c8bd0fee1530f1c204cc5c68b427a3498dbdad)
- Represent non-stringified JSON request body as an [object Object] string [`5c6b055`](https://github.com/JakeChampion/fetch/commit/5c6b055e6ae6f718f416c94bfcdc89693d0abdcb)
- Fix eslint and eslint-plugin-github dependency conflicts [`190e698`](https://github.com/JakeChampion/fetch/commit/190e698f8e737ad751a11de60f6b8b3301fa557b)

## [v3.5.0](https://github.com/JakeChampion/fetch/compare/v3.4.1...v3.5.0)

> 6 November 2020

- Fixes #748 [`#748`](https://github.com/JakeChampion/fetch/issues/748)
- Create lock.yml [`8767781`](https://github.com/JakeChampion/fetch/commit/87677811d543cfb44b124e026b50f710e95017ec)

## [v3.4.1](https://github.com/JakeChampion/fetch/compare/v3.4.0...v3.4.1)

> 7 September 2020

- Add npmignore file to ensure we always publish the dist directory [`7ca02eb`](https://github.com/JakeChampion/fetch/commit/7ca02eb0234b0a61fd711d922b2e69d3c5390516)
- Make the clean task remove the dist directory and the default task create it [`fd23745`](https://github.com/JakeChampion/fetch/commit/fd23745f3474cd23d88e5128d8bc74813be1aff0)

## [v3.4.0](https://github.com/JakeChampion/fetch/compare/v3.3.1...v3.4.0)

> 7 August 2020

- Use globalThis as the global object if it exists [`96c2651`](https://github.com/JakeChampion/fetch/commit/96c26512608a0081d493df4fc17da4394bd1b410)

## [v3.3.1](https://github.com/JakeChampion/fetch/compare/v3.3.0...v3.3.1)

> 4 August 2020

- rename variable to no longer shadow over function of same name [`c5db762`](https://github.com/JakeChampion/fetch/commit/c5db7621c3b1530683b8f706388d4ac210a2db02)
- remove semicolon to pass linting [`f264aa5`](https://github.com/JakeChampion/fetch/commit/f264aa5704f7431c429ec16e6fdd3c7034c7f2d9)

## [v3.3.0](https://github.com/JakeChampion/fetch/compare/v3.2.0...v3.3.0)

> 4 August 2020

- Make Response.arrayBuffer() always resolve with a `ArrayBuffer` [`#801`](https://github.com/github/fetch/issues/801)
- Stop using top-level `this` to stop rollup warning [`#802`](https://github.com/github/fetch/issues/802)
- Recommend an AbortController polyfill which is fully synchronous [`#800`](https://github.com/github/fetch/issues/800)
- Add keepalive caveat [`#780`](https://github.com/github/fetch/issues/780)
- Throw a TypeError if Request or Response functions are called without `new` [`5ef028d`](https://github.com/JakeChampion/fetch/commit/5ef028d61f6c1543603cdacbe0f8a0f00d5957c0)
- If headers are passed in via a Record then do not normalise the header names as part of the request [`b65ed60`](https://github.com/JakeChampion/fetch/commit/b65ed608604492d605df2d62cd4c5050e2a8d508)
- Update fetch.js [`37b55c2`](https://github.com/JakeChampion/fetch/commit/37b55c27413b902cef4e629892424ae469fb1ea2)

## [v3.2.0](https://github.com/JakeChampion/fetch/compare/v3.1.1...v3.2.0)

> 9 July 2020

- Detect if DOMException exists via typeof instead of trying to call it and catching the exception which may get thrown [`#724`](https://github.com/github/fetch/issues/724)
- use `this` if `self` is not defined [`#657`](https://github.com/github/fetch/issues/657)
- create variable called `global` which is either `self` or `this` [`a0783a5`](https://github.com/JakeChampion/fetch/commit/a0783a5571018191578cc08d5b3bac61a0b64562)
- Add support for no-cache and no-store via a cache-busting querystring parameter [`a0dcd85`](https://github.com/JakeChampion/fetch/commit/a0dcd853f8ed29d06a022f92c87c303bd0e1f1bf)
- make global `this` correct when using rollup [`6e9fc0e`](https://github.com/JakeChampion/fetch/commit/6e9fc0ee026dd89d864c3d176c57789ee5615114)

## [v3.1.1](https://github.com/JakeChampion/fetch/compare/v3.1.0...v3.1.1)

> 8 July 2020

- check if Content-Type header exists prior to  examining the value [`#792`](https://github.com/JakeChampion/fetch/pull/792)
- Move from Travis to GitHub Actions [`#793`](https://github.com/JakeChampion/fetch/pull/793)

## [v3.1.0](https://github.com/JakeChampion/fetch/compare/v3.0.1...v3.1.0)

> 29 June 2020

## [v3.0.1](https://github.com/JakeChampion/fetch/compare/v3.0.0...v3.0.1)

> 8 July 2020

- check if Content-Type header exists prior to  examining the value [`#792`](https://github.com/JakeChampion/fetch/pull/792)
- Move from Travis to GitHub Actions [`#793`](https://github.com/JakeChampion/fetch/pull/793)
- Co-authored-by: Jake Champion <me@jakechampion.name> [`#575`](https://github.com/JakeChampion/fetch/pull/575)
- work around IE XHR bug with '' URL Fixes #618 [`#619`](https://github.com/JakeChampion/fetch/pull/619)
- Allow exclamation mark as valid header character [`#745`](https://github.com/JakeChampion/fetch/pull/745)
- Avoid blob conversion for specific requests [`#752`](https://github.com/JakeChampion/fetch/pull/752)
- Compatibility for fetch-mock using proxy-pollyfill [`#736`](https://github.com/JakeChampion/fetch/pull/736)
- Change default statusText for Response [`#698`](https://github.com/JakeChampion/fetch/pull/698)
- Document more common pitfalls in the README [`#734`](https://github.com/JakeChampion/fetch/pull/734)
- field name can not by empty [`#684`](https://github.com/JakeChampion/fetch/pull/684)
- work around IE XHR bug with '' URL Fixes #618 (#619) [`#618`](https://github.com/JakeChampion/fetch/issues/618)
- Clarify what parts of the standard we don't want to implement [`#661`](https://github.com/JakeChampion/fetch/issues/661)
- Document more caveats [`9a0bce2`](https://github.com/JakeChampion/fetch/commit/9a0bce23454cdd5beefd9d4c599664003573e581)
- Fix issue #533 [`7f030fa`](https://github.com/JakeChampion/fetch/commit/7f030fab4d79433204331cefe365f5fbbab9e992)
- Compatibility with newer eslint-plugin-github [`1821b74`](https://github.com/JakeChampion/fetch/commit/1821b74b808152d4d6e787c21165f2d569c2a7c4)


### [v3.0.0](https://github.com/JakeChampion/fetch/compare/v2.0.4...v3.0.0)

> 7 September 2018

- Add flow definitions [`#654`](https://github.com/JakeChampion/fetch/pull/654)
- Match spec behavior re: unsupported body type [`#651`](https://github.com/JakeChampion/fetch/pull/651)
- Update Karma and detect available browsers when testing [`#652`](https://github.com/JakeChampion/fetch/pull/652)
- Adopt Contributor Covenant Code of Conduct [`#649`](https://github.com/JakeChampion/fetch/pull/649)
- Change `credentials` default value to `same-origin` [`#640`](https://github.com/JakeChampion/fetch/pull/640)
- Switch test suite from PhantomJS to Karma [`#626`](https://github.com/JakeChampion/fetch/pull/626)
- Support abort API [`#592`](https://github.com/JakeChampion/fetch/pull/592)
- build/distribute as UMD [`#616`](https://github.com/JakeChampion/fetch/pull/616)
- Test signal reuse. Add AbortSignal polyfill. [`#2`](https://github.com/JakeChampion/fetch/pull/2)
- Clear abort event listener for all xhr completion states. [`#1`](https://github.com/JakeChampion/fetch/pull/1)
- Expand install & importing documentation [`#569`](https://github.com/JakeChampion/fetch/issues/569)
- Match spec behavior re: unsupported body type [`#576`](https://github.com/JakeChampion/fetch/issues/576)
- Run test files through prettier [`0a57487`](https://github.com/JakeChampion/fetch/commit/0a5748775d99f882172375693f56761383f8faf3)
- Unwrap `fetch.js` to be a clean module file [`8aec47c`](https://github.com/JakeChampion/fetch/commit/8aec47cb6c67a9a321f1eb07457f70fc46235610)
- Switch from PhantomJS to Karma + Chrome/Firefox for testing [`b539589`](https://github.com/JakeChampion/fetch/commit/b53958904649bfeb784083b9b7e0b89902c7d30e)

## [v2.0.4](https://github.com/JakeChampion/fetch/compare/v2.0.3...v2.0.4)

> 29 March 2018

- Create CONTRIBUTING.md [`#604`](https://github.com/JakeChampion/fetch/pull/604)
- Tweak the wording of the “Read this first” section [`#553`](https://github.com/JakeChampion/fetch/pull/553)
- Allow undefined Response status [`#534`](https://github.com/JakeChampion/fetch/pull/534)
- Ensure cookies aren't sent if `credentials: omit` [`#526`](https://github.com/JakeChampion/fetch/pull/526)
- Added yarn command as option to installation [`#492`](https://github.com/JakeChampion/fetch/pull/492)
- Add global replace for processing raw headers [`#496`](https://github.com/JakeChampion/fetch/pull/496)
- Added safari to native fetch browser support. [`#469`](https://github.com/JakeChampion/fetch/pull/469)
- Support obs-fold as header delimiter [`#491`](https://github.com/JakeChampion/fetch/pull/491)
- Tweak the wording of "Read this first" [`54dc3f8`](https://github.com/JakeChampion/fetch/commit/54dc3f823fe3e6452da8d19bf7aad7eda4cd1dd8)
- Add test for undefined Response status [`0ecdd40`](https://github.com/JakeChampion/fetch/commit/0ecdd40c50d4bcdfd4d2a09448a6d01089dc182a)
- Fix cookie test with newer versions of Node [`7831671`](https://github.com/JakeChampion/fetch/commit/7831671b172435c52064f588cf7145236fecf5f2)

## [v2.0.3](https://github.com/JakeChampion/fetch/compare/v2.0.2...v2.0.3)

> 2 March 2017

- Accept array in Headers constructor [`#485`](https://github.com/JakeChampion/fetch/pull/485)
- Improve README language [`#483`](https://github.com/JakeChampion/fetch/pull/483)
- Fix grammar mistake in README [`#468`](https://github.com/JakeChampion/fetch/pull/468)
- Remove bower version from release instructions [`5cc72dd`](https://github.com/JakeChampion/fetch/commit/5cc72dd734bfd459a61a61e472c90654d71afc91)
- Remove extra punctuation [`eebaa2a`](https://github.com/JakeChampion/fetch/commit/eebaa2a1bc21eeba98ee00c9f94a0a4c2007cff1)
- Fetch 2.0.3 [`d4ed806`](https://github.com/JakeChampion/fetch/commit/d4ed806fdcbdeaef707d27f6c88943f0336a647d)

## [v2.0.2](https://github.com/JakeChampion/fetch/compare/v2.0.1...v2.0.2)

> 19 January 2017

- Treat any non-Request arg to `new Request()` as string url [`#465`](https://github.com/JakeChampion/fetch/pull/465)
- Support Tolerance Provision when parsing headers [`#449`](https://github.com/JakeChampion/fetch/pull/449)
- Add test for cloning GET request [`#440`](https://github.com/JakeChampion/fetch/issues/440)
- Detect broken URL support in PhantomJS and skip test [`b285e61`](https://github.com/JakeChampion/fetch/commit/b285e61fbc4dc21d4b5f7a498046bdff585abf1b)
- Remove secrets [`9240ef4`](https://github.com/JakeChampion/fetch/commit/9240ef453a1ebc3670b8377f9deb771d684e7f68)
- fetch 2.0.2 [`b337f95`](https://github.com/JakeChampion/fetch/commit/b337f9578fa8e21fa5c9fe8d6eb74baaa43a1c02)

## [v2.0.1](https://github.com/JakeChampion/fetch/compare/v2.0.0...v2.0.1)

> 17 November 2016

- Fix misspelling of [ae]ffect [`#432`](https://github.com/JakeChampion/fetch/pull/432)
- Fix reading ArrayBuffer into string on older browsers [`6f8529e`](https://github.com/JakeChampion/fetch/commit/6f8529e4c5ceacc92c97f58a9bc6538879978f3c)
- Only define `arrayBuffer()` if Blob is also supported [`3d3bb0c`](https://github.com/JakeChampion/fetch/commit/3d3bb0ca72172b224e8101c0a5264adc41f53929)
- Display uncaught errors on the test results page [`54ec096`](https://github.com/JakeChampion/fetch/commit/54ec0965c25a9889e5ba597421faf7b0790de026)

### [v2.0.0](https://github.com/JakeChampion/fetch/compare/v1.1.1...v2.0.0)

> 14 November 2016

- Change Headers multiple value handling for spec compatibility [`#429`](https://github.com/JakeChampion/fetch/pull/429)
- Firefox now implements `Headers.forEach` natively [`468f877`](https://github.com/JakeChampion/fetch/commit/468f877e4447a2b267236f2f8fa4f1492c0dd20b)
- fetch 2.0.0 [`c576d61`](https://github.com/JakeChampion/fetch/commit/c576d61fee39bb34699bbe870460b6120011150a)

## [v1.1.1](https://github.com/JakeChampion/fetch/compare/v1.1.0...v1.1.1)

> 17 November 2016

- Fix reading ArrayBuffer into string on older browsers [`1ddcadb`](https://github.com/JakeChampion/fetch/commit/1ddcadb2418c4cf0b206857f424a9af58c0ed57f)
- Only define `arrayBuffer()` if Blob is also supported [`c2556f3`](https://github.com/JakeChampion/fetch/commit/c2556f3ed41a238df4ee384fd8e4c404f3971e64)
- fetch 1.1.1 [`f7a5148`](https://github.com/JakeChampion/fetch/commit/f7a514829820fc77c0f884c74cf2d36356a781c0)

## [v1.1.0](https://github.com/JakeChampion/fetch/compare/v1.0.0...v1.1.0)

> 14 November 2016

- Support ArrayBufferView types as POST body [`#430`](https://github.com/JakeChampion/fetch/pull/430)
- Spec compatibility for Request/Response constructors and cloning [`#428`](https://github.com/JakeChampion/fetch/pull/428)
- Improve Readme [`#427`](https://github.com/JakeChampion/fetch/pull/427)
- Fix grammar [`#408`](https://github.com/JakeChampion/fetch/pull/408)
- Fixed typo in README.md [`#403`](https://github.com/JakeChampion/fetch/pull/403)
- make X-Request-URL header case-insensitive [`#384`](https://github.com/JakeChampion/fetch/pull/384)
- Better error handling with Saucelabs [`#354`](https://github.com/JakeChampion/fetch/pull/354)
- Update Webpack section in README [`#331`](https://github.com/JakeChampion/fetch/pull/331)
- Attach FileReader event handlers before calling its `read*` method [`#353`](https://github.com/JakeChampion/fetch/issues/353)
- Default Response status is 200 OK [`#376`](https://github.com/JakeChampion/fetch/issues/376)
- Support ArrayBuffer in BodyInit [`#350`](https://github.com/JakeChampion/fetch/issues/350)
- Avoid consuming body when cloning [`#308`](https://github.com/JakeChampion/fetch/issues/308) [`#335`](https://github.com/JakeChampion/fetch/issues/335)
- Rework parsing of raw response HTTP headers [`#422`](https://github.com/JakeChampion/fetch/issues/422)
- Allow reusing the same GET Request instance multiple times [`#411`](https://github.com/JakeChampion/fetch/issues/411)
- Always construct a new Headers instance in Response [`#416`](https://github.com/JakeChampion/fetch/issues/416)
- Rework the Installation section [`#415`](https://github.com/JakeChampion/fetch/issues/415)
- More information about cookies [`#393`](https://github.com/JakeChampion/fetch/issues/393)
- It looks like Safari 10 didn't ship with native fetch [`#401`](https://github.com/JakeChampion/fetch/issues/401)
- Reorganize tests with the new "fetch method" suite [`ba7ffda`](https://github.com/JakeChampion/fetch/commit/ba7ffda7b2bf6b9183fbca04120c042babd17f00)
- Share identical tests between Request & Response [`9a04a06`](https://github.com/JakeChampion/fetch/commit/9a04a0667b92dba567746b26b553ab9a329fa94d)
- ArrayBuffer can now be consumed through `blob()`/`text()` [`9a703ba`](https://github.com/JakeChampion/fetch/commit/9a703ba38ff3bddc94c8929c1e8fae5d766462cd)

### [v1.0.0](https://github.com/JakeChampion/fetch/compare/v0.11.1...v1.0.0)

> 28 April 2016

- refactor Header iterator methods [`#317`](https://github.com/JakeChampion/fetch/pull/317)
- Add ES2015+ example [`#287`](https://github.com/JakeChampion/fetch/pull/287)
- Switch to `mocha-phantomjs-core` and system PhantomJS [`#314`](https://github.com/JakeChampion/fetch/pull/314)
- Reject promise on request timeout [`#306`](https://github.com/JakeChampion/fetch/pull/306)
- Use uppercase methods in README [`#272`](https://github.com/JakeChampion/fetch/pull/272)
- Guard against `xhr.getAllResponseHeaders()` being `null` [`#289`](https://github.com/JakeChampion/fetch/pull/289)
- Add support for URLSearchParams POST body [`#304`](https://github.com/JakeChampion/fetch/pull/304)
- Add Headers iterators [`#295`](https://github.com/JakeChampion/fetch/pull/295)
- fix example [`#282`](https://github.com/JakeChampion/fetch/pull/282)
- Drop IE-specific status codes workarounds and require IE10+ [`#270`](https://github.com/JakeChampion/fetch/pull/270)
- Reject promise on request timeout [`#294`](https://github.com/JakeChampion/fetch/issues/294)
- Make Headers iterable if Symbol is available [`a1b7674`](https://github.com/JakeChampion/fetch/commit/a1b7674b6942d4265ea47b74760a486b2bf5e3da)
- Support URLSearchParams POST body [`d77810a`](https://github.com/JakeChampion/fetch/commit/d77810a15c78bbbaf2defd4ea3af6db21c8d117f)
- Fix formatting [`edb7c73`](https://github.com/JakeChampion/fetch/commit/edb7c7336f53b5c0e08ef0ccb37e43c8d9de778f)

## [v0.11.1](https://github.com/JakeChampion/fetch/compare/v0.11.0...v0.11.1)

> 5 May 2016

- Reject promise on request timeout [`#294`](https://github.com/JakeChampion/fetch/issues/294)
- Fix formatting [`3fc66ed`](https://github.com/JakeChampion/fetch/commit/3fc66edc4c0f61a60b2debfca276a7a8140aa2c9)
- Fetch 0.11.1 [`7d9a11d`](https://github.com/JakeChampion/fetch/commit/7d9a11deec5c0ea2d453390be647ba52695166f8)
- Guard against `xhr.getAllResponseHeaders()` being `null` [`8deb829`](https://github.com/JakeChampion/fetch/commit/8deb8296681f6ad0990e0af47b99d71f2a1d1701)

## [v0.11.0](https://github.com/JakeChampion/fetch/compare/v0.10.1...v0.11.0)

> 19 January 2016

- Handle cases where `self` isn't defined [`#253`](https://github.com/JakeChampion/fetch/pull/253)
- Exercise both polyfill and native `fetch` in test suite [`#258`](https://github.com/JakeChampion/fetch/pull/258)
- Make fetch add a `Content-Type` header based on the type of the body. [`1e4a615`](https://github.com/JakeChampion/fetch/commit/1e4a6151e6a1f4e2e792f7faa0a028498a7be973)
- Cleanup in determining implicit content-type [`3b5dc9c`](https://github.com/JakeChampion/fetch/commit/3b5dc9c17f2be9ca1a2e7030dd8209f0b150bc70)
- Render main test suite as root resource of test server [`b043384`](https://github.com/JakeChampion/fetch/commit/b043384e2d7b68b10172a64e5c5b00a593cd41c3)

## [v0.10.1](https://github.com/JakeChampion/fetch/compare/v0.10.0...v0.10.1)

> 2 November 2015

- Allow making a POST request with an ArrayBuffer body [`#227`](https://github.com/JakeChampion/fetch/pull/227)
- Run Sauce Labs CI for pull requests [`#220`](https://github.com/JakeChampion/fetch/pull/220)
- Streamline Sauce Labs API interactions [`07dc8ae`](https://github.com/JakeChampion/fetch/commit/07dc8ae4cc9a46ad4af35c99a8bdc0b83fbae28b)
- Download and start Sauce Connect manually [`b3885b4`](https://github.com/JakeChampion/fetch/commit/b3885b4eceb4a818e7f8d2d290f89f4d8eaeb0d3)
- Switch to my credentials for npm publish from CI [`e0a4851`](https://github.com/JakeChampion/fetch/commit/e0a48518734aac116f49962c825522ab99da8338)

## [v0.10.0](https://github.com/JakeChampion/fetch/compare/v0.9.0...v0.10.0)

> 12 October 2015

- Remove moot `version` property from bower.json [`#159`](https://github.com/JakeChampion/fetch/pull/159)
- Use absolute URL in Response.redirect test [`#219`](https://github.com/JakeChampion/fetch/pull/219)
- Support Response.error() and Response.redirect() [`#212`](https://github.com/JakeChampion/fetch/pull/212)
- Reject the Promise returned by fetch() when Request ctor throws [`#217`](https://github.com/JakeChampion/fetch/pull/217)
- Fix incorrect assertion [`#216`](https://github.com/JakeChampion/fetch/pull/216)
- Remove superfluous assignment [`#213`](https://github.com/JakeChampion/fetch/pull/213)
- Add webpack usage link. [`#195`](https://github.com/JakeChampion/fetch/pull/195)
- Allow passing a Request instance to Request constructor [`#179`](https://github.com/JakeChampion/fetch/pull/179)
- Properly convert undefined/null header values to strings. [`#156`](https://github.com/JakeChampion/fetch/pull/156)
- Code of Conduct [`#174`](https://github.com/JakeChampion/fetch/pull/174)
- Improve documentation for `fetch` caveats [`#164`](https://github.com/JakeChampion/fetch/pull/164)
- Opt into new Travis infrastructure [`#158`](https://github.com/JakeChampion/fetch/pull/158)
- Merge branch 'orphan-black' [`#209`](https://github.com/JakeChampion/fetch/issues/209) [`#185`](https://github.com/JakeChampion/fetch/issues/185)
- Add include credentials example. [`#205`](https://github.com/JakeChampion/fetch/issues/205)
- Add `Request.clone()` and `Response.clone()` methods [`46705f7`](https://github.com/JakeChampion/fetch/commit/46705f798e1c6e6c9ef03156a8ec8f64c9971d69)
- Fix and simplify `Request.clone()` [`fd362dd`](https://github.com/JakeChampion/fetch/commit/fd362ddb1dcb6918cf8203c99f40d7fed019afc4)
- Expand caveats with notes about cookies [`184b647`](https://github.com/JakeChampion/fetch/commit/184b64719f90ba0cc88c6eeb43c9a2a7ea5fb726)

## [v0.9.0](https://github.com/JakeChampion/fetch/compare/v0.8.2...v0.9.0)

> 29 May 2015

- Implement Headers#forEach correctly [`#150`](https://github.com/JakeChampion/fetch/pull/150)
- Test forEach. [`2f442ce`](https://github.com/JakeChampion/fetch/commit/2f442cebf84f3e7057e2d94408a1b9ec4643c783)
- Fix forEach parameters. [`0449483`](https://github.com/JakeChampion/fetch/commit/0449483b4ab1e9184e74302e1c6fb17e9cd44a75)
- Accept a thisArg forEach parameter. [`bd2fe03`](https://github.com/JakeChampion/fetch/commit/bd2fe03140cfdaf4bd38ca5b4798c775a58b6fd5)

## [v0.8.2](https://github.com/JakeChampion/fetch/compare/v0.8.1...v0.8.2)

> 19 May 2015

- Set xhr.withCredentials after xhr.open called. [`a847967`](https://github.com/JakeChampion/fetch/commit/a847967a0314a574dada2c31e1825f75ed6dc24a)
- Only support standard options. [`cc9f4b0`](https://github.com/JakeChampion/fetch/commit/cc9f4b0e3e2aaa8cf751dfc2098e58a94fc71e59)
- Fetch 0.8.2 [`0b3e1d7`](https://github.com/JakeChampion/fetch/commit/0b3e1d7c41c75359a3e0b771741ebc2a8823da38)

## [v0.8.1](https://github.com/JakeChampion/fetch/compare/v0.8.0...v0.8.1)

> 4 May 2015

- Fetch 0.8.1 [`09c316d`](https://github.com/JakeChampion/fetch/commit/09c316d2450c08fde129336438b3a44de4e8177c)
- Ignore script/ dir [`2e39db1`](https://github.com/JakeChampion/fetch/commit/2e39db1b02c5453ed9c3e156f5d68240f0e76907)

## [v0.8.0](https://github.com/JakeChampion/fetch/compare/v0.7.0...v0.8.0)

> 4 May 2015

- only define _initBody once [`#136`](https://github.com/JakeChampion/fetch/pull/136)
- remove un-needed promise allocations in example [`#120`](https://github.com/JakeChampion/fetch/pull/120)
- Headers constructor in Response constructor [`#107`](https://github.com/JakeChampion/fetch/pull/107)
- Sauce: IE9 [`#102`](https://github.com/JakeChampion/fetch/pull/102)
- Sauce Labs: IE 11 [`#101`](https://github.com/JakeChampion/fetch/pull/101)
- Sauce Labs [`#99`](https://github.com/JakeChampion/fetch/pull/99)
- Add a convenience `ok` getter on `Response` [`#82`](https://github.com/JakeChampion/fetch/pull/82)
- Follow spec on Headers to throw TypeError, add tests for Headers [`#85`](https://github.com/JakeChampion/fetch/pull/85)
- adds .npmignore [`#84`](https://github.com/JakeChampion/fetch/pull/84)
- node.js module link [`#81`](https://github.com/JakeChampion/fetch/pull/81)
- Add script runner for saucelabs [`47fc7d5`](https://github.com/JakeChampion/fetch/commit/47fc7d5a8431505af8dec8326e5d081219ad7d6a)
- Split app and server [`29cc5dc`](https://github.com/JakeChampion/fetch/commit/29cc5dc74441679c63e99145ba841f8abd29da17)
- More scripty [`ba1214a`](https://github.com/JakeChampion/fetch/commit/ba1214acaf766eba9e0a268de495d8b9c9e295c1)

## [v0.7.0](https://github.com/JakeChampion/fetch/compare/v0.6.1...v0.7.0)

> 24 January 2015

- Centralise the checks for blob and form data support [`#78`](https://github.com/JakeChampion/fetch/pull/78)
- If cors, with credentials [`#77`](https://github.com/JakeChampion/fetch/pull/77)
- Add metadata for repository, bugs and license [`#67`](https://github.com/JakeChampion/fetch/pull/67)
- Declare deliberate "async=true" on XMLHttpRequest open [`#74`](https://github.com/JakeChampion/fetch/pull/74)
- Fix typo in npm install instructions [`#71`](https://github.com/JakeChampion/fetch/pull/71)
- Improve Request/Response BodyInit consuming [`#70`](https://github.com/JakeChampion/fetch/pull/70)
- Fix up body consuming on request [`fbfa9e3`](https://github.com/JakeChampion/fetch/commit/fbfa9e332039d3d4e4e91da6038729d061455ef1)
- Throw TypeError if body is given for GET or HEAD [`5ce5677`](https://github.com/JakeChampion/fetch/commit/5ce56771da78d341561887df2bb65f78425333c4)
- A few more tests and typo fix. [`614b2aa`](https://github.com/JakeChampion/fetch/commit/614b2aab10525f8e2a55124fdb33b374b61a0c87)

## [v0.6.1](https://github.com/JakeChampion/fetch/compare/v0.6.0...v0.6.1)

> 15 January 2015

- Add charset content-type tests [`7474e42`](https://github.com/JakeChampion/fetch/commit/7474e42af467bcd843f97a3def92b0c7d63e4f48)
- Add additional body init and consume test coverage [`9d58648`](https://github.com/JakeChampion/fetch/commit/9d586486e50a79551b1d12178b3408d1fd57cb35)
- Fix X-Request-URL on CORS requests [`4525329`](https://github.com/JakeChampion/fetch/commit/4525329eb075da74fd7585d4ea8ddeabc97b17a4)

## [v0.6.0](https://github.com/JakeChampion/fetch/compare/v0.5.0...v0.6.0)

> 12 January 2015

- Suspect this api key was wrong [`#63`](https://github.com/JakeChampion/fetch/pull/63)
- Use responseText on IE9 which lacks XHR2 support [`eeb53d3`](https://github.com/JakeChampion/fetch/commit/eeb53d391dcb12a2d77765bf602fc45427112687)
- Tidy up binary file reader [`7436589`](https://github.com/JakeChampion/fetch/commit/74365897619b533fe7b9080568ad43e852130974)
- Use `xhr.responseType = 'blob'` to preserve binary data. [`080358d`](https://github.com/JakeChampion/fetch/commit/080358ddb26ff37cfd27caf730af9cd3c184bc42)

## [v0.5.0](https://github.com/JakeChampion/fetch/compare/v0.4.0...v0.5.0)

> 12 January 2015

- Enable travis to publish to npm. [`#57`](https://github.com/JakeChampion/fetch/pull/57)
- Make Headers case insensitive though lowercasing. [`#62`](https://github.com/JakeChampion/fetch/pull/62)
- Support credentials [`#56`](https://github.com/JakeChampion/fetch/pull/56)
- Switch to Mocha [`#59`](https://github.com/JakeChampion/fetch/pull/59)
- Test Atomic HTTP redirect handling [`#55`](https://github.com/JakeChampion/fetch/pull/55)
- Mark FormData support as optional [`#54`](https://github.com/JakeChampion/fetch/pull/54)
- Add promise test helper [`#53`](https://github.com/JakeChampion/fetch/pull/53)
- Test in web worker [`#51`](https://github.com/JakeChampion/fetch/issues/51)
- Group tests [`ecd8600`](https://github.com/JakeChampion/fetch/commit/ecd8600932b0d8495d646df0d6fa74874cd57713)
- Switch to mocha [`cbd6c66`](https://github.com/JakeChampion/fetch/commit/cbd6c66fe4bbda1b63cef54c299c2081c4b50955)
- Skip tests in phantomjs [`8a4b620`](https://github.com/JakeChampion/fetch/commit/8a4b62027ea7c590861364e853eccb5f52a8991b)

## [v0.4.0](https://github.com/JakeChampion/fetch/compare/v0.3.2...v0.4.0)

> 29 December 2014

- Assign to self [`#52`](https://github.com/JakeChampion/fetch/pull/52)
- Web Workers support [`#48`](https://github.com/JakeChampion/fetch/pull/48)
- Align used flag error message to Chrome's implementation [`#44`](https://github.com/JakeChampion/fetch/pull/44)
- Add missing quote. [`#40`](https://github.com/JakeChampion/fetch/issues/40)
- Align bodyUsed error message to Chrome's implementation [`e414284`](https://github.com/JakeChampion/fetch/commit/e4142843fab42705cc65d526bf86b1662da7f338)
- Avoid testing implementation specific error messages [`cc42153`](https://github.com/JakeChampion/fetch/commit/cc4215367d2298f779ee9a5ab3f8c9cb6207c8c5)
- Set esnext option [`3ebc441`](https://github.com/JakeChampion/fetch/commit/3ebc44129997dbc2e331450b6e876b8f6e36437b)

## [v0.3.2](https://github.com/JakeChampion/fetch/compare/v0.3.1...v0.3.2)

> 24 November 2014

- FormData should only able to consume once [`#38`](https://github.com/JakeChampion/fetch/pull/38)
- Test formData body consumption. [`4a7e655`](https://github.com/JakeChampion/fetch/commit/4a7e655b4f0361524d16adb16148c1fe0f2f1f0f)
- Fetch 0.3.2 [`830231e`](https://github.com/JakeChampion/fetch/commit/830231e5682175fe04088b291192f72c59aed998)

## [v0.3.1](https://github.com/JakeChampion/fetch/compare/v0.3.0...v0.3.1)

> 21 November 2014

- Reject promise with TypeError for network failures. [`#36`](https://github.com/JakeChampion/fetch/pull/36)
- Reject example promise with an Error. [`#35`](https://github.com/JakeChampion/fetch/issues/35)
- Fetch 0.3.1 [`eb3f9b2`](https://github.com/JakeChampion/fetch/commit/eb3f9b2b1fa7804883cbf853102944847d65e204)

## [v0.3.0](https://github.com/JakeChampion/fetch/compare/v0.2.1...v0.3.0)

> 13 November 2014

- IE 9+ fixes [`#28`](https://github.com/JakeChampion/fetch/pull/28)
- Move body to _body to prevent direct access [`#32`](https://github.com/JakeChampion/fetch/pull/32)
- Remove form encoded object body. [`#30`](https://github.com/JakeChampion/fetch/pull/30)
- Document how to use in Browserify… [`#29`](https://github.com/JakeChampion/fetch/pull/29)
- Auto-detect available port when running headless tests [`#27`](https://github.com/JakeChampion/fetch/pull/27)
- Shell highlight [`#24`](https://github.com/JakeChampion/fetch/pull/24)
- use shorthand npm installation [`#23`](https://github.com/JakeChampion/fetch/pull/23)
- Add name/version/main so it can be installed from npm. [`#22`](https://github.com/JakeChampion/fetch/pull/22)
- Add example of success and error handlers [`#18`](https://github.com/JakeChampion/fetch/pull/18)
- Test Server [`#13`](https://github.com/JakeChampion/fetch/pull/13)
- Travis [`#12`](https://github.com/JakeChampion/fetch/pull/12)
- Add test server [`3316bda`](https://github.com/JakeChampion/fetch/commit/3316bdaf03e4ef2eef35161913efca9a8bd31457)
- Uppercase the HTTP method name [`c71f1dd`](https://github.com/JakeChampion/fetch/commit/c71f1dd9bb0a84453ad2e14cb4a638cc735e1344)
- Skip blob tests on phantom [`c02cad2`](https://github.com/JakeChampion/fetch/commit/c02cad221e0d239dd79e42f2d91c9bb48501776c)

## [v0.2.1](https://github.com/JakeChampion/fetch/compare/v0.2.0...v0.2.1)

> 15 October 2014

- Use of `Promise.reject` as a constructor [`#10`](https://github.com/JakeChampion/fetch/pull/10)
- Fixed uncaught error when a body was consumed more than once. [`e428559`](https://github.com/JakeChampion/fetch/commit/e428559a68f5c9d445bf2fb5a91fb3f7e35b2f5d)
- Fetch 0.2.1 [`8160180`](https://github.com/JakeChampion/fetch/commit/81601803ec9fd1ffa29f4d527b12e586dd9840c1)

## [v0.2.0](https://github.com/JakeChampion/fetch/compare/v0.1.0...v0.2.0)

> 15 October 2014

- Parse form encoded response body [`#8`](https://github.com/JakeChampion/fetch/pull/8)
- Allow body to be consumed only once [`#7`](https://github.com/JakeChampion/fetch/pull/7)
- throw proper errors [`#5`](https://github.com/JakeChampion/fetch/pull/5)
- Allow body to be consumed a single time. [`c7a27dc`](https://github.com/JakeChampion/fetch/commit/c7a27dc12f43f398f8159db98f3745d8c3320515)
- Parse form encoded response body. [`60271ce`](https://github.com/JakeChampion/fetch/commit/60271cef8aa13641ba99c197f974fbb4a5f77c57)
- Extract consumed function. [`a709976`](https://github.com/JakeChampion/fetch/commit/a7099768059b9befbb656c6b1e3944bad8a97327)

## v0.1.0

> 13 October 2014

- 🐶 [`c1af6de`](https://github.com/JakeChampion/fetch/commit/c1af6de1e87d2753203f2e750e48b625fac2e24a)
- Start tests. [`44e796a`](https://github.com/JakeChampion/fetch/commit/44e796a874dd88bfd365cc6dcddfe973daeec08a)
- Add readme. [`7eee89d`](https://github.com/JakeChampion/fetch/commit/7eee89d15ee21e762a04b4c773fcc3d7d50a13f7)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource+fetch@github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Thank you for your interest in contributing to our `fetch` polyfill!

Note that we only accept features that are also described in the official [fetch
specification][]. However, the aim of this project is not to implement the
complete specification; just the parts that are feasible to emulate using
XMLHttpRequest. See [Caveats][] for some examples of features that we are
unlikely to implement.

Contributions to this project are [released][tos] to the public under the
[project's open source license](LICENSE).

## Running tests

Running `npm test` will:

1. Build the `dist/` files;
1. Run the test suite in headless Chrome & Firefox;
1. Run the same test suite in Web Worker mode.

When editing tests or implementation, keep `npm run karma` running:

- You can connect additional browsers by navigating to `http://localhost:9876/`;
- Changes to [test.js](test/test.js) will automatically re-run the tests in all
  connected browsers;
- When changing [fetch.js](fetch.js), re-run tests by executing `make`;
- Re-run specific tests with `./node_modules/.bin/karma run -- --grep=<PATTERN>`.

## Submitting a pull request

1. [Fork][fork] and clone the repository;
1. Create a new branch: `git checkout -b my-branch-name`;
1. Make your change, push to your fork and [submit a pull request][pr];
1. Pat your self on the back and wait for your pull request to be reviewed.

Here are a few things you can do that will increase the likelihood of your pull
request being accepted:

- Keep your change as focused as possible. If there are multiple changes you
  would like to make that are not dependent upon each other, consider submitting
  them as separate pull requests.
- Write a [good commit message][].

## Resources

- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)


  [fetch specification]: https://fetch.spec.whatwg.org
  [tos]: https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license
  [fork]: https://github.com/github/fetch/fork
  [pr]: https://github.com/github/fetch/compare
  [good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
  [caveats]: https://github.github.io/fetch/#caveats


================================================
FILE: LICENSE
================================================
Copyright (c) 2014-2023 GitHub, Inc.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: Makefile
================================================
test: lint dist/fetch.umd.js

lint: node_modules/
	./node_modules/.bin/eslint --report-unused-disable-directives *.js test/*.js

dist/fetch.umd.js: fetch.js rollup.config.js node_modules/
	./node_modules/.bin/rollup -c

dist/fetch.umd.js.flow: fetch.js.flow
	cp $< $@

node_modules/:
	npm install

clean:
	rm -rf ./bower_components ./node_modules ./dist

.PHONY: clean lint test make dist/fetch.umd.js dist/fetch.umd.js.flow


================================================
FILE: README.md
================================================
# window.fetch polyfill

[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/JakeChampion/fetch/badge)](https://securityscorecards.dev/viewer/?uri=github.com/JakeChampion/fetch)

The `fetch()` function is a Promise-based mechanism for programmatically making
web requests in the browser. This project is a polyfill that implements a subset
of the standard [Fetch specification][], enough to make `fetch` a viable
replacement for most uses of XMLHttpRequest in traditional web applications.

## Table of Contents

* [Read this first](#read-this-first)
* [Installation](#installation)
* [Usage](#usage)
  * [Importing](#importing)
  * [HTML](#html)
  * [JSON](#json)
  * [Response metadata](#response-metadata)
  * [Post form](#post-form)
  * [Post JSON](#post-json)
  * [File upload](#file-upload)
  * [Caveats](#caveats)
    * [Handling HTTP error statuses](#handling-http-error-statuses)
    * [Sending cookies](#sending-cookies)
    * [Receiving cookies](#receiving-cookies)
    * [Redirect modes](#redirect-modes)
    * [Obtaining the Response URL](#obtaining-the-response-url)
    * [Aborting requests](#aborting-requests)
* [Browser Support](#browser-support)

## Read this first

* If you believe you found a bug with how `fetch` behaves in your browser,
  please **don't open an issue in this repository** unless you are testing in
  an old version of a browser that doesn't support `window.fetch` natively.
  Make sure you read this _entire_ readme, especially the [Caveats](#caveats)
  section, as there's probably a known work-around for an issue you've found.
  This project is a _polyfill_, and since all modern browsers now implement the
  `fetch` function natively, **no code from this project** actually takes any
  effect there. See [Browser support](#browser-support) for detailed
  information.

* If you have trouble **making a request to another domain** (a different
  subdomain or port number also constitutes another domain), please familiarize
  yourself with all the intricacies and limitations of [CORS][] requests.
  Because CORS requires participation of the server by implementing specific
  HTTP response headers, it is often nontrivial to set up or debug. CORS is
  exclusively handled by the browser's internal mechanisms which this polyfill
  cannot influence.

* This project **doesn't work under Node.js environments**. It's meant for web
  browsers only. You should ensure that your application doesn't try to package
  and run this on the server.

* If you have an idea for a new feature of `fetch`, **submit your feature
  requests** to the [specification's repository](https://github.com/whatwg/fetch/issues).
  We only add features and APIs that are part of the [Fetch specification][].

## Installation

```
npm install whatwg-fetch --save
```

You will also need a Promise polyfill for [older browsers](https://caniuse.com/promises).
We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill)
for its small size and Promises/A+ compatibility.

## Usage

### Importing

Importing will automatically polyfill `window.fetch` and related APIs:

```javascript
import 'whatwg-fetch'

window.fetch(...)
```

If for some reason you need to access the polyfill implementation, it is
available via exports:

```javascript
import {fetch as fetchPolyfill} from 'whatwg-fetch'

window.fetch(...)   // use native browser version
fetchPolyfill(...)  // use polyfill implementation
```

This approach can be used to, for example, use [abort
functionality](#aborting-requests) in browsers that implement a native but
outdated version of fetch that doesn't support aborting.

For use with webpack, add this package in the `entry` configuration option
before your application entry point:

```javascript
entry: ['whatwg-fetch', ...]
```

### HTML

```javascript
fetch('/users.html')
  .then(function(response) {
    return response.text()
  }).then(function(body) {
    document.body.innerHTML = body
  })
```

### JSON

```javascript
fetch('/users.json')
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    console.log('parsed json', json)
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })
```

### Response metadata

```javascript
fetch('/users.json').then(function(response) {
  console.log(response.headers.get('Content-Type'))
  console.log(response.headers.get('Date'))
  console.log(response.status)
  console.log(response.statusText)
})
```

### Post form

```javascript
var form = document.querySelector('form')

fetch('/users', {
  method: 'POST',
  body: new FormData(form)
})
```

### Post JSON

```javascript
fetch('/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})
```

### File upload

```javascript
var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})
```

### Caveats

* The Promise returned from `fetch()` **won't reject on HTTP error status**
  even if the response is an HTTP 404 or 500. Instead, it will resolve normally,
  and it will only reject on network failure or if anything prevented the
  request from completing.

* For maximum browser compatibility when it comes to sending & receiving
  cookies, always supply the `credentials: 'same-origin'` option instead of
  relying on the default. See [Sending cookies](#sending-cookies).

* Not all Fetch standard options are supported in this polyfill. For instance,
  [`redirect`](#redirect-modes) and
  `cache` directives are ignored.
  
* `keepalive` is not supported because it would involve making a synchronous XHR, which is something this project is not willing to do. See [issue #700](https://github.com/github/fetch/issues/700#issuecomment-484188326) for more information.

#### Handling HTTP error statuses

To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx
status, define a custom response handler:

```javascript
function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

function parseJSON(response) {
  return response.json()
}

fetch('/users')
  .then(checkStatus)
  .then(parseJSON)
  .then(function(data) {
    console.log('request succeeded with JSON response', data)
  }).catch(function(error) {
    console.log('request failed', error)
  })
```

#### Sending cookies

For [CORS][] requests, use `credentials: 'include'` to allow sending credentials
to other domains:

```javascript
fetch('https://example.com:1234/users', {
  credentials: 'include'
})
```

The default value for `credentials` is "same-origin".

The default for `credentials` wasn't always the same, though. The following
versions of browsers implemented an older version of the fetch specification
where the default was "omit":

* Firefox 39-60
* Chrome 42-67
* Safari 10.1-11.1.2

If you target these browsers, it's advisable to always specify `credentials:
'same-origin'` explicitly with all fetch requests instead of relying on the
default:

```javascript
fetch('/users', {
  credentials: 'same-origin'
})
```

Note: due to [limitations of
XMLHttpRequest](https://github.com/github/fetch/pull/56#issuecomment-68835992),
using `credentials: 'omit'` is not respected for same domains in browsers where
this polyfill is active. Cookies will always be sent to same domains in older
browsers.

#### Receiving cookies

As with XMLHttpRequest, the `Set-Cookie` response header returned from the
server is a [forbidden header name][] and therefore can't be programmatically
read with `response.headers.get()`. Instead, it's the browser's responsibility
to handle new cookies being set (if applicable to the current URL). Unless they
are HTTP-only, new cookies will be available through `document.cookie`.

#### Redirect modes

The Fetch specification defines these values for [the `redirect`
option](https://fetch.spec.whatwg.org/#concept-request-redirect-mode): "follow"
(the default), "error", and "manual".

Due to limitations of XMLHttpRequest, only the "follow" mode is available in
browsers where this polyfill is active.

#### Obtaining the Response URL

Due to limitations of XMLHttpRequest, the `response.url` value might not be
reliable after HTTP redirects on older browsers.

The solution is to configure the server to set the response HTTP header
`X-Request-URL` to the current URL after any redirect that might have happened.
It should be safe to set it unconditionally.

``` ruby
# Ruby on Rails controller example
response.headers['X-Request-URL'] = request.url
```

This server workaround is necessary if you need reliable `response.url` in
Firefox < 32, Chrome < 37, Safari, or IE.

#### Aborting requests

This polyfill supports
[the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch).
However, aborting a fetch requires use of two additional DOM APIs:
[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and
[AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
Typically, browsers that do not support fetch will also not support
AbortController or AbortSignal. Consequently, you will need to include
[an additional polyfill](https://www.npmjs.com/package/yet-another-abortcontroller-polyfill)
for these APIs to abort fetches:

```js
import 'yet-another-abortcontroller-polyfill'
import {fetch} from 'whatwg-fetch'

// use native browser implementation if it supports aborting
const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch

const controller = new AbortController()

abortableFetch('/avatars', {
  signal: controller.signal
}).catch(function(ex) {
  if (ex.name === 'AbortError') {
    console.log('request aborted')
  }
})

// some time later...
controller.abort()
```

## Browser Support

- Chrome
- Firefox
- Safari 6.1+
- Internet Explorer 10+

Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native
implementations of `window.fetch`, therefore the code from this polyfill doesn't
have any effect on those browsers. If you believe you've encountered an error
with how `window.fetch` is implemented in any of these browsers, you should file
an issue with that browser vendor instead of this project.


  [fetch specification]: https://fetch.spec.whatwg.org
  [cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
    "Cross-origin resource sharing"
  [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
    "Cross-site request forgery"
  [forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
  [releases]: https://github.com/github/fetch/releases


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Security updates are applied only to the latest release.

## Reporting a Vulnerability

If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/JakeChampion/fetch/security/advisories/new).
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.


================================================
FILE: bower.json
================================================
{
  "name": "fetch",
  "main": "fetch.js",
  "ignore": [
    ".*",
    "*.md",
    "examples/",
    "Makefile",
    "package.json",
    "script/",
    "test/"
  ]
}


================================================
FILE: fetch.js
================================================
/* eslint-disable no-prototype-builtins */
var g =
  (typeof globalThis !== 'undefined' && globalThis) ||
  (typeof self !== 'undefined' && self) ||
  // eslint-disable-next-line no-undef
  (typeof global !== 'undefined' && global) ||
  {}

var support = {
  searchParams: 'URLSearchParams' in g,
  iterable: 'Symbol' in g && 'iterator' in Symbol,
  blob:
    'FileReader' in g &&
    'Blob' in g &&
    (function() {
      try {
        new Blob()
        return true
      } catch (e) {
        return false
      }
    })(),
  formData: 'FormData' in g,
  arrayBuffer: 'ArrayBuffer' in g
}

function isDataView(obj) {
  return obj && DataView.prototype.isPrototypeOf(obj)
}

if (support.arrayBuffer) {
  var viewClasses = [
    '[object Int8Array]',
    '[object Uint8Array]',
    '[object Uint8ClampedArray]',
    '[object Int16Array]',
    '[object Uint16Array]',
    '[object Int32Array]',
    '[object Uint32Array]',
    '[object Float32Array]',
    '[object Float64Array]'
  ]

  var isArrayBufferView =
    ArrayBuffer.isView ||
    function(obj) {
      return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
    }
}

function normalizeName(name) {
  if (typeof name !== 'string') {
    name = String(name)
  }
  if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
    throw new TypeError('Invalid character in header field name: "' + name + '"')
  }
  return name.toLowerCase()
}

function normalizeValue(value) {
  if (typeof value !== 'string') {
    value = String(value)
  }
  return value
}

// Build a destructive iterator for the value list
function iteratorFor(items) {
  var iterator = {
    next: function() {
      var value = items.shift()
      return {done: value === undefined, value: value}
    }
  }

  if (support.iterable) {
    iterator[Symbol.iterator] = function() {
      return iterator
    }
  }

  return iterator
}

export function Headers(headers) {
  this.map = {}

  if (headers instanceof Headers) {
    headers.forEach(function(value, name) {
      this.append(name, value)
    }, this)
  } else if (Array.isArray(headers)) {
    headers.forEach(function(header) {
      if (header.length != 2) {
        throw new TypeError('Headers constructor: expected name/value pair to be length 2, found' + header.length)
      }
      this.append(header[0], header[1])
    }, this)
  } else if (headers) {
    Object.getOwnPropertyNames(headers).forEach(function(name) {
      this.append(name, headers[name])
    }, this)
  }
}

Headers.prototype.append = function(name, value) {
  name = normalizeName(name)
  value = normalizeValue(value)
  var oldValue = this.map[name]
  this.map[name] = oldValue ? oldValue + ', ' + value : value
}

Headers.prototype['delete'] = function(name) {
  delete this.map[normalizeName(name)]
}

Headers.prototype.get = function(name) {
  name = normalizeName(name)
  return this.has(name) ? this.map[name] : null
}

Headers.prototype.has = function(name) {
  return this.map.hasOwnProperty(normalizeName(name))
}

Headers.prototype.set = function(name, value) {
  this.map[normalizeName(name)] = normalizeValue(value)
}

Headers.prototype.forEach = function(callback, thisArg) {
  for (var name in this.map) {
    if (this.map.hasOwnProperty(name)) {
      callback.call(thisArg, this.map[name], name, this)
    }
  }
}

Headers.prototype.keys = function() {
  var items = []
  this.forEach(function(value, name) {
    items.push(name)
  })
  return iteratorFor(items)
}

Headers.prototype.values = function() {
  var items = []
  this.forEach(function(value) {
    items.push(value)
  })
  return iteratorFor(items)
}

Headers.prototype.entries = function() {
  var items = []
  this.forEach(function(value, name) {
    items.push([name, value])
  })
  return iteratorFor(items)
}

if (support.iterable) {
  Headers.prototype[Symbol.iterator] = Headers.prototype.entries
}

function consumed(body) {
  if (body._noBody) return
  if (body.bodyUsed) {
    return Promise.reject(new TypeError('Already read'))
  }
  body.bodyUsed = true
}

function fileReaderReady(reader) {
  return new Promise(function(resolve, reject) {
    reader.onload = function() {
      resolve(reader.result)
    }
    reader.onerror = function() {
      reject(reader.error)
    }
  })
}

function readBlobAsArrayBuffer(blob) {
  var reader = new FileReader()
  var promise = fileReaderReady(reader)
  reader.readAsArrayBuffer(blob)
  return promise
}

function readBlobAsText(blob) {
  var reader = new FileReader()
  var promise = fileReaderReady(reader)
  var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type)
  var encoding = match ? match[1] : 'utf-8'
  reader.readAsText(blob, encoding)
  return promise
}

function readArrayBufferAsText(buf) {
  var view = new Uint8Array(buf)
  var chars = new Array(view.length)

  for (var i = 0; i < view.length; i++) {
    chars[i] = String.fromCharCode(view[i])
  }
  return chars.join('')
}

function bufferClone(buf) {
  if (buf.slice) {
    return buf.slice(0)
  } else {
    var view = new Uint8Array(buf.byteLength)
    view.set(new Uint8Array(buf))
    return view.buffer
  }
}

function Body() {
  this.bodyUsed = false

  this._initBody = function(body) {
    /*
      fetch-mock wraps the Response object in an ES6 Proxy to
      provide useful test harness features such as flush. However, on
      ES5 browsers without fetch or Proxy support pollyfills must be used;
      the proxy-pollyfill is unable to proxy an attribute unless it exists
      on the object before the Proxy is created. This change ensures
      Response.bodyUsed exists on the instance, while maintaining the
      semantic of setting Request.bodyUsed in the constructor before
      _initBody is called.
    */
    // eslint-disable-next-line no-self-assign
    this.bodyUsed = this.bodyUsed
    this._bodyInit = body
    if (!body) {
      this._noBody = true;
      this._bodyText = ''
    } else if (typeof body === 'string') {
      this._bodyText = body
    } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
      this._bodyBlob = body
    } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
      this._bodyFormData = body
    } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
      this._bodyText = body.toString()
    } else if (support.arrayBuffer && support.blob && isDataView(body)) {
      this._bodyArrayBuffer = bufferClone(body.buffer)
      // IE 10-11 can't handle a DataView body.
      this._bodyInit = new Blob([this._bodyArrayBuffer])
    } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
      this._bodyArrayBuffer = bufferClone(body)
    } else {
      this._bodyText = body = Object.prototype.toString.call(body)
    }

    if (!this.headers.get('content-type')) {
      if (typeof body === 'string') {
        this.headers.set('content-type', 'text/plain;charset=UTF-8')
      } else if (this._bodyBlob && this._bodyBlob.type) {
        this.headers.set('content-type', this._bodyBlob.type)
      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
        this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
      }
    }
  }

  if (support.blob) {
    this.blob = function() {
      var rejected = consumed(this)
      if (rejected) {
        return rejected
      }

      if (this._bodyBlob) {
        return Promise.resolve(this._bodyBlob)
      } else if (this._bodyArrayBuffer) {
        return Promise.resolve(new Blob([this._bodyArrayBuffer]))
      } else if (this._bodyFormData) {
        throw new Error('could not read FormData body as blob')
      } else {
        return Promise.resolve(new Blob([this._bodyText]))
      }
    }
  }

  this.arrayBuffer = function() {
    if (this._bodyArrayBuffer) {
      var isConsumed = consumed(this)
      if (isConsumed) {
        return isConsumed
      } else if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
        return Promise.resolve(
          this._bodyArrayBuffer.buffer.slice(
            this._bodyArrayBuffer.byteOffset,
            this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
          )
        )
      } else {
        return Promise.resolve(this._bodyArrayBuffer)
      }
    } else if (support.blob) {
      return this.blob().then(readBlobAsArrayBuffer)
    } else {
      throw new Error('could not read as ArrayBuffer')
    }
  }

  this.text = function() {
    var rejected = consumed(this)
    if (rejected) {
      return rejected
    }

    if (this._bodyBlob) {
      return readBlobAsText(this._bodyBlob)
    } else if (this._bodyArrayBuffer) {
      return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
    } else if (this._bodyFormData) {
      throw new Error('could not read FormData body as text')
    } else {
      return Promise.resolve(this._bodyText)
    }
  }

  if (support.formData) {
    this.formData = function() {
      return this.text().then(decode)
    }
  }

  this.json = function() {
    return this.text().then(JSON.parse)
  }

  return this
}

// HTTP methods whose capitalization should be normalized
var methods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']

function normalizeMethod(method) {
  var upcased = method.toUpperCase()
  return methods.indexOf(upcased) > -1 ? upcased : method
}

export function Request(input, options) {
  if (!(this instanceof Request)) {
    throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
  }

  options = options || {}
  var body = options.body

  if (input instanceof Request) {
    if (input.bodyUsed) {
      throw new TypeError('Already read')
    }
    this.url = input.url
    this.credentials = input.credentials
    if (!options.headers) {
      this.headers = new Headers(input.headers)
    }
    this.method = input.method
    this.mode = input.mode
    this.signal = input.signal
    if (!body && input._bodyInit != null) {
      body = input._bodyInit
      input.bodyUsed = true
    }
  } else {
    this.url = String(input)
  }

  this.credentials = options.credentials || this.credentials || 'same-origin'
  if (options.headers || !this.headers) {
    this.headers = new Headers(options.headers)
  }
  this.method = normalizeMethod(options.method || this.method || 'GET')
  this.mode = options.mode || this.mode || null
  this.signal = options.signal || this.signal || (function () {
    if ('AbortController' in g) {
      var ctrl = new AbortController();
      return ctrl.signal;
    }
  }());
  this.referrer = null

  if ((this.method === 'GET' || this.method === 'HEAD') && body) {
    throw new TypeError('Body not allowed for GET or HEAD requests')
  }
  this._initBody(body)

  if (this.method === 'GET' || this.method === 'HEAD') {
    if (options.cache === 'no-store' || options.cache === 'no-cache') {
      // Search for a '_' parameter in the query string
      var reParamSearch = /([?&])_=[^&]*/
      if (reParamSearch.test(this.url)) {
        // If it already exists then set the value with the current time
        this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime())
      } else {
        // Otherwise add a new '_' parameter to the end with the current time
        var reQueryString = /\?/
        this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime()
      }
    }
  }
}

Request.prototype.clone = function() {
  return new Request(this, {body: this._bodyInit})
}

function decode(body) {
  var form = new FormData()
  body
    .trim()
    .split('&')
    .forEach(function(bytes) {
      if (bytes) {
        var split = bytes.split('=')
        var name = split.shift().replace(/\+/g, ' ')
        var value = split.join('=').replace(/\+/g, ' ')
        form.append(decodeURIComponent(name), decodeURIComponent(value))
      }
    })
  return form
}

function parseHeaders(rawHeaders) {
  var headers = new Headers()
  // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
  // https://tools.ietf.org/html/rfc7230#section-3.2
  var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
  // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
  // https://github.com/github/fetch/issues/748
  // https://github.com/zloirock/core-js/issues/751
  preProcessedHeaders
    .split('\r')
    .map(function(header) {
      return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header
    })
    .forEach(function(line) {
      var parts = line.split(':')
      var key = parts.shift().trim()
      if (key) {
        var value = parts.join(':').trim()
        try {
          headers.append(key, value)
        } catch (error) {
          console.warn('Response ' + error.message)
        }
      }
    })
  return headers
}

Body.call(Request.prototype)

export function Response(bodyInit, options) {
  if (!(this instanceof Response)) {
    throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
  }
  if (!options) {
    options = {}
  }

  this.type = 'default'
  this.status = options.status === undefined ? 200 : options.status
  if (this.status < 200 || this.status > 599) {
    throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].")
  }
  this.ok = this.status >= 200 && this.status < 300
  this.statusText = options.statusText === undefined ? '' : '' + options.statusText
  this.headers = new Headers(options.headers)
  this.url = options.url || ''
  this._initBody(bodyInit)
}

Body.call(Response.prototype)

Response.prototype.clone = function() {
  return new Response(this._bodyInit, {
    status: this.status,
    statusText: this.statusText,
    headers: new Headers(this.headers),
    url: this.url
  })
}

Response.error = function() {
  var response = new Response(null, {status: 200, statusText: ''})
  response.ok = false
  response.status = 0
  response.type = 'error'
  return response
}

var redirectStatuses = [301, 302, 303, 307, 308]

Response.redirect = function(url, status) {
  if (redirectStatuses.indexOf(status) === -1) {
    throw new RangeError('Invalid status code')
  }

  return new Response(null, {status: status, headers: {location: url}})
}

export var DOMException = g.DOMException
try {
  new DOMException()
} catch (err) {
  DOMException = function(message, name) {
    this.message = message
    this.name = name
    var error = Error(message)
    this.stack = error.stack
  }
  DOMException.prototype = Object.create(Error.prototype)
  DOMException.prototype.constructor = DOMException
}

export function fetch(input, init) {
  return new Promise(function(resolve, reject) {
    var request = new Request(input, init)

    if (request.signal && request.signal.aborted) {
      return reject(new DOMException('Aborted', 'AbortError'))
    }

    var xhr = new XMLHttpRequest()

    function abortXhr() {
      xhr.abort()
    }

    xhr.onload = function() {
      var options = {
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || '')
      }
      // This check if specifically for when a user fetches a file locally from the file system
      // Only if the status is out of a normal range
      if (request.url.indexOf('file://') === 0 && (xhr.status < 200 || xhr.status > 599)) {
        options.status = 200;
      } else {
        options.status = xhr.status;
      }
      options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
      var body = 'response' in xhr ? xhr.response : xhr.responseText
      setTimeout(function() {
        resolve(new Response(body, options))
      }, 0)
    }

    xhr.onerror = function() {
      setTimeout(function() {
        reject(new TypeError('Network request failed'))
      }, 0)
    }

    xhr.ontimeout = function() {
      setTimeout(function() {
        reject(new TypeError('Network request timed out'))
      }, 0)
    }

    xhr.onabort = function() {
      setTimeout(function() {
        reject(new DOMException('Aborted', 'AbortError'))
      }, 0)
    }

    function fixUrl(url) {
      try {
        return url === '' && g.location.href ? g.location.href : url
      } catch (e) {
        return url
      }
    }

    xhr.open(request.method, fixUrl(request.url), true)

    if (request.credentials === 'include') {
      xhr.withCredentials = true
    } else if (request.credentials === 'omit') {
      xhr.withCredentials = false
    }

    if ('responseType' in xhr) {
      if (support.blob) {
        xhr.responseType = 'blob'
      } else if (
        support.arrayBuffer
      ) {
        xhr.responseType = 'arraybuffer'
      }
    }

    if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers || (g.Headers && init.headers instanceof g.Headers))) {
      var names = [];
      Object.getOwnPropertyNames(init.headers).forEach(function(name) {
        names.push(normalizeName(name))
        xhr.setRequestHeader(name, normalizeValue(init.headers[name]))
      })
      request.headers.forEach(function(value, name) {
        if (names.indexOf(name) === -1) {
          xhr.setRequestHeader(name, value)
        }
      })
    } else {
      request.headers.forEach(function(value, name) {
        xhr.setRequestHeader(name, value)
      })
    }

    if (request.signal) {
      request.signal.addEventListener('abort', abortXhr)

      xhr.onreadystatechange = function() {
        // DONE (success or failure)
        if (xhr.readyState === 4) {
          request.signal.removeEventListener('abort', abortXhr)
        }
      }
    }

    xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
  })
}

fetch.polyfill = true

if (!g.fetch) {
  g.fetch = fetch
  g.Headers = Headers
  g.Request = Request
  g.Response = Response
}


================================================
FILE: fetch.js.flow
================================================
/* @flow strict */

type CredentialsType = 'omit' | 'same-origin' | 'include'

type ResponseType =  'default' | 'error'

type BodyInit = string | URLSearchParams | FormData | Blob | ArrayBuffer | $ArrayBufferView

type RequestInfo = Request | URL | string

type RequestOptions = {|
  body?: ?BodyInit;

  credentials?: CredentialsType;
  headers?: HeadersInit;
  method?: string;
  mode?: string;
  referrer?: string;
  signal?: ?AbortSignal;
|}

type ResponseOptions = {|
  status?: number;
  statusText?: string;
  headers?: HeadersInit;
|}

type HeadersInit = Headers | {[string]: string}

// https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L902-L914
declare class Headers {
  @@iterator(): Iterator<[string, string]>;
  constructor(init?: HeadersInit): void;
  append(name: string, value: string): void;
  delete(name: string): void;
  entries(): Iterator<[string, string]>;
  forEach((value: string, name: string, headers: Headers) => any, thisArg?: any): void;
  get(name: string): null | string;
  has(name: string): boolean;
  keys(): Iterator<string>;
  set(name: string, value: string): void;
  values(): Iterator<string>;
}

// https://github.com/facebook/flow/pull/6548
interface AbortSignal {
  aborted: boolean;
  addEventListener(type: string, listener: (Event) => mixed, options?: EventListenerOptionsOrUseCapture): void;
  removeEventListener(type: string, listener: (Event) => mixed, options?: EventListenerOptionsOrUseCapture): void;
}

// https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L994-L1018
// unsupported in polyfill:
// - cache
// - integrity
// - redirect
// - referrerPolicy
declare class Request {
  constructor(input: RequestInfo, init?: RequestOptions): void;
  clone(): Request;

  url: string;

  credentials: CredentialsType;
  headers: Headers;
  method: string;
  mode: ModeType;
  referrer: string;
  signal: ?AbortSignal;

  // Body methods and attributes
  bodyUsed: boolean;

  arrayBuffer(): Promise<ArrayBuffer>;
  blob(): Promise<Blob>;
  formData(): Promise<FormData>;
  json(): Promise<any>;
  text(): Promise<string>;
}

// https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L968-L992
// unsupported in polyfill:
// - body
// - redirected
// - trailer
declare class Response {
  constructor(input?: ?BodyInit, init?: ResponseOptions): void;
  clone(): Response;
  static error(): Response;
  static redirect(url: string, status?: number): Response;

  type: ResponseType;
  url: string;
  ok: boolean;
  status: number;
  statusText: string;
  headers: Headers;

  // Body methods and attributes
  bodyUsed: boolean;

  arrayBuffer(): Promise<ArrayBuffer>;
  blob(): Promise<Blob>;
  formData(): Promise<FormData>;
  json(): Promise<any>;
  text(): Promise<string>;
}

declare class DOMException extends Error {
  constructor(message?: string, name?: string): void;
}

declare module.exports: {
  fetch(input: RequestInfo, init?: RequestOptions): Promise<Response>;
  Headers: typeof Headers;
  Request: typeof Request;
  Response: typeof Response;
  DOMException: typeof DOMException;
}


================================================
FILE: package.json
================================================
{
  "name": "whatwg-fetch",
  "description": "A window.fetch polyfill.",
  "version": "3.6.20",
  "main": "./dist/fetch.umd.js",
  "module": "./fetch.js",
  "repository": "github/fetch",
  "license": "MIT",
  "devDependencies": {
    "abortcontroller-polyfill": "^1.1.9",
    "auto-changelog": "^2.4.0",
    "chai": "^4.1.2",
    "eslint": "^7.20.0",
    "karma": "^3.0.0",
    "karma-chai": "^0.1.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-detect-browsers": "^2.3.2",
    "karma-firefox-launcher": "^1.1.0",
    "karma-mocha": "^1.3.0",
    "karma-safari-launcher": "^1.0.0",
    "karma-safaritechpreview-launcher": "0.0.6",
    "mocha": "^4.0.1",
    "prettier": "^1.19.1",
    "promise-polyfill": "6.0.2",
    "rollup": "^0.59.1",
    "url-search-params": "0.6.1"
  },
  "files": [
    "LICENSE",
    "dist/fetch.umd.js",
    "dist/fetch.umd.js.flow",
    "fetch.js",
    "fetch.js.flow"
  ],
  "scripts": {
    "karma": "karma start ./test/karma.config.js --no-single-run --auto-watch",
    "prepare": "make dist/fetch.umd.js dist/fetch.umd.js.flow",
    "pretest": "make",
    "test": "karma start ./test/karma.config.js && karma start ./test/karma-worker.config.js",
    "version": "auto-changelog -p && git add CHANGELOG.md"
  }
}


================================================
FILE: prettier.config.js
================================================
/* eslint-env node */
module.exports = require('eslint-plugin-github/prettier.config')


================================================
FILE: rollup.config.js
================================================
export default {
  input: 'fetch.js',
  output: {
    file: 'dist/fetch.umd.js',
    format: 'umd',
    name: 'WHATWGFetch'
  }
}


================================================
FILE: test/karma-worker.config.js
================================================
/* eslint-env node */
const parentConfig = require('./karma.config')

module.exports = function(config) {
  parentConfig(config)
  config.set({
    frameworks: ['detectBrowsers', 'mocha'],
    files: [
      'test/worker-adapter.js',
      {
        pattern: '{test,dist}/*.js',
        included: false
      },
      {
        pattern: 'node_modules/{mocha,chai,abortcontroller-polyfill/dist}/*.js',
        included: false,
        watched: false
      }
    ]
  })
}


================================================
FILE: test/karma.config.js
================================================
/* eslint-env node */
const serverEndpoints = require('./server')

module.exports = function(config) {
  config.set({
    basePath: '..',
    frameworks: ['detectBrowsers', 'mocha', 'chai'],
    detectBrowsers: {
      preferHeadless: true,
      usePhantomJS: false,
      postDetection: availableBrowsers =>
        availableBrowsers
          .filter(
            browser =>
              !process.env.CI || !browser.startsWith('Chromium') || !availableBrowsers.some(b => b.startsWith('Chrome'))
          )
          .map(browser => (browser.startsWith('Chrom') ? `${browser}NoSandbox` : browser))
    },
    client: {
      mocha: {
        ui: 'tdd'
      }
    },
    files: [
      'node_modules/promise-polyfill/promise.js',
      'node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js',
      'node_modules/url-search-params/build/url-search-params.max.js',
      'dist/fetch.umd.js',
      'test/test.js'
    ],
    reporters: process.env.CI ? ['dots'] : ['progress'],
    port: 9876,
    colors: true,
    logLevel: process.env.CI ? config.LOG_WARN : config.LOG_INFO,
    autoWatch: false,
    singleRun: true,
    concurrency: Infinity,
    customLaunchers: {
      ChromeHeadlessNoSandbox: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      },
      ChromiumHeadlessNoSandbox: {
        base: 'ChromiumHeadless',
        flags: ['--no-sandbox']
      }
    },
    beforeMiddleware: ['custom'],
    plugins: [
      'karma-*',
      {
        'middleware:custom': ['value', serverEndpoints]
      }
    ]
  })
}


================================================
FILE: test/server.js
================================================
/* eslint-env node */
const url = require('url')
const querystring = require('querystring')

const routes = {
  '/request': function(res, req) {
    res.writeHead(200, {'Content-Type': 'application/json'})
    var data = ''
    req.on('data', function(c) {
      data += c
    })
    req.on('end', function() {
      res.end(
        JSON.stringify({
          method: req.method,
          url: req.url,
          headers: req.headers,
          data: data
        })
      )
    })
  },
  '/hello': function(res, req) {
    res.writeHead(200, {
      'Content-Type': 'text/plain',
      'X-Request-URL': 'http://' + req.headers.host + req.url
    })
    res.end('hi')
  },
  '/hello/utf8': function(res) {
    res.writeHead(200, {
      'Content-Type': 'text/plain; charset=utf-8'
    })
    // "hello"
    var buf = Buffer.from([104, 101, 108, 108, 111])
    res.end(buf)
  },
  '/hello/utf16le': function(res) {
    res.writeHead(200, {
      'Content-Type': 'text/plain; charset=utf-16le'
    })
    // "hello"
    var buf = Buffer.from([104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
    res.end(buf)
  },
  '/binary': function(res) {
    res.writeHead(200, {'Content-Type': 'application/octet-stream'})
    var buf = Buffer.alloc(256)
    for (var i = 0; i < 256; i++) {
      buf[i] = i
    }
    res.end(buf)
  },
  '/redirect/301': function(res) {
    res.writeHead(301, {Location: '/hello'})
    res.end()
  },
  '/redirect/302': function(res) {
    res.writeHead(302, {Location: '/hello'})
    res.end()
  },
  '/redirect/303': function(res) {
    res.writeHead(303, {Location: '/hello'})
    res.end()
  },
  '/redirect/307': function(res) {
    res.writeHead(307, {Location: '/hello'})
    res.end()
  },
  '/redirect/308': function(res) {
    res.writeHead(308, {Location: '/hello'})
    res.end()
  },
  '/boom': function(res) {
    res.writeHead(500, {'Content-Type': 'text/plain'})
    res.end('boom')
  },
  '/empty': function(res) {
    res.writeHead(204)
    res.end()
  },
  '/slow': function(res) {
    setTimeout(function() {
      res.writeHead(200, {'Cache-Control': 'no-cache, must-revalidate'})
      res.end()
    }, 100)
  },
  '/error': function(res) {
    res.destroy()
  },
  '/form': function(res) {
    res.writeHead(200, {'Content-Type': 'application/x-www-form-urlencoded'})
    res.end('number=1&space=one+two&empty=&encoded=a%2Bb&')
  },
  '/json': function(res) {
    res.writeHead(200, {'Content-Type': 'application/json'})
    res.end(JSON.stringify({name: 'Hubot', login: 'hubot'}))
  },
  '/json-error': function(res) {
    res.writeHead(200, {'Content-Type': 'application/json'})
    res.end('not json {')
  },
  '/cookie': function(res, req) {
    var setCookie, cookie
    var params = querystring.parse(url.parse(req.url).query)
    if (params.name && params.value) {
      setCookie = [params.name, params.value].join('=')
    }
    if (params.name) {
      cookie = querystring.parse(req.headers['cookie'], '; ')[params.name]
    }
    res.writeHead(200, {
      'Content-Type': 'text/plain',
      'Set-Cookie': setCookie || ''
    })
    res.end(cookie)
  },
  '/headers': function(res) {
    res.writeHead(200, {
      Date: 'Mon, 13 Oct 2014 21:02:27 GMT',
      'Content-Type': 'text/html; charset=utf-8'
    })
    res.end()
  }
}

module.exports = function(req, res, next) {
  const path = url.parse(req.url).pathname
  const route = routes[path]
  if (route) {
    route(res, req)
  } else {
    next()
  }
}


================================================
FILE: test/test.js
================================================
/* eslint-env mocha */
/* globals chai assert FileReaderSync assert WHATWGFetch */
var IEorEdge = /Edge\//.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent)
var Chrome = /Chrome\//.test(navigator.userAgent) && !IEorEdge
var Safari = /Safari\//.test(navigator.userAgent) && !IEorEdge && !Chrome

var support = {
  url: (function(url) {
    try {
      return new URL(url).toString() === url
    } catch (e) {
      return false
    }
  })('http://example.com/'),
  blob:
    'FileReader' in self &&
    'Blob' in self &&
    (function() {
      try {
        new Blob()
        return true
      } catch (e) {
        return false
      }
    })(),
  formData: 'FormData' in self,
  arrayBuffer: 'ArrayBuffer' in self,
  aborting: 'signal' in new Request(''),
  permanentRedirect: !/Trident/.test(navigator.userAgent)
}

function readBlobAsText(blob) {
  if ('FileReader' in self) {
    return new Promise(function(resolve, reject) {
      var reader = new FileReader()
      reader.onload = function() {
        resolve(reader.result)
      }
      reader.onerror = function() {
        reject(reader.error)
      }
      reader.readAsText(blob)
    })
  } else if ('FileReaderSync' in self) {
    return new FileReaderSync().readAsText(blob)
  } else {
    throw new ReferenceError('FileReader is not defined')
  }
}

function readBlobAsBytes(blob) {
  if ('FileReader' in self) {
    return new Promise(function(resolve, reject) {
      var reader = new FileReader()
      reader.onload = function() {
        var view = new Uint8Array(reader.result)
        resolve(Array.prototype.slice.call(view))
      }
      reader.onerror = function() {
        reject(reader.error)
      }
      reader.readAsArrayBuffer(blob)
    })
  } else if ('FileReaderSync' in self) {
    return new FileReaderSync().readAsArrayBuffer(blob)
  } else {
    throw new ReferenceError('FileReader is not defined')
  }
}

function arrayBufferFromText(text) {
  var buf = new ArrayBuffer(text.length)
  var view = new Uint8Array(buf)

  for (var i = 0; i < text.length; i++) {
    view[i] = text.charCodeAt(i)
  }
  return buf
}

function readArrayBufferAsText(buf) {
  var view = new Uint8Array(buf)
  var chars = new Array(view.length)

  for (var i = 0; i < view.length; i++) {
    chars[i] = String.fromCharCode(view[i])
  }
  return chars.join('')
}

var preservedGlobals = {}
var keepGlobals = ['fetch', 'Headers', 'Request', 'Response']
var exercise = ['polyfill']

// If native fetch implementation exists, replace it with the polyfilled
// version at first. The native implementation will be restored before the
// additional `native` pass of the test suite.
if (!self.fetch.polyfill) {
  keepGlobals.forEach(function(name) {
    preservedGlobals[name] = self[name]
    self[name] = WHATWGFetch[name]
  })
  exercise.push('native')
}

var slice = Array.prototype.slice

function featureDependent(testOrSuite, condition) {
  (condition ? testOrSuite : testOrSuite.skip).apply(this, slice.call(arguments, 2))
}

exercise.forEach(function(exerciseMode) {
  suite(exerciseMode, function() {
    if (exerciseMode === 'native') {
      suiteSetup(function() {
        keepGlobals.forEach(function(name) {
          self[name] = preservedGlobals[name]
        })
      })
    }

    var nativeChrome = Chrome && exerciseMode === 'native'
    var nativeSafari = Safari && exerciseMode === 'native'
    var nativeEdge = /Edge\//.test(navigator.userAgent) && exerciseMode === 'native'
    var firefox = navigator.userAgent.match(/Firefox\/(\d+)/)
    var brokenFF = firefox && firefox[1] <= 56 && exerciseMode === 'native'
    var emptyDefaultStatusText =
      exerciseMode !== 'native' || (exerciseMode === 'native' && (Chrome || (firefox && firefox[1] >= 67)))
    var polyfillFirefox = firefox && exerciseMode === 'polyfill'
    var omitSafari =
      Safari && exerciseMode === 'native' && navigator.userAgent.match(/Version\/(\d+\.\d+)/)[1] <= '11.1'

    // https://fetch.spec.whatwg.org/#concept-bodyinit-extract
    function testBodyExtract(factory) {
      suite('body extract', function() {
        var expected = 'Hello World!'
        var inputs = [['type USVString', expected]]
        if (support.blob) {
          inputs.push(['type Blob', new Blob([expected])])
        }
        if (support.arrayBuffer) {
          inputs = inputs.concat([
            ['type ArrayBuffer', arrayBufferFromText(expected)],
            ['type TypedArray', new Uint8Array(arrayBufferFromText(expected))],
            ['type DataView', new DataView(arrayBufferFromText(expected))]
          ])
        }

        inputs.forEach(function(input) {
          var typeLabel = input[0],
            body = input[1]

          suite(typeLabel, function() {
            featureDependent(test, support.blob, 'consume as blob', function() {
              var r = factory(body)
              return r
                .blob()
                .then(readBlobAsText)
                .then(function(text) {
                  assert.equal(text, expected)
                })
            })

            test('consume as text', function() {
              var r = factory(body)
              return r.text().then(function(text) {
                assert.equal(text, expected)
              })
            })

            featureDependent(test, support.arrayBuffer, 'consume as array buffer', function() {
              var r = factory(body)
              return r
                .arrayBuffer()
                .then(readArrayBufferAsText)
                .then(function(text) {
                  assert.equal(text, expected)
                })
            })
          })
        })
      })
    }

    // https://fetch.spec.whatwg.org/#headers-class
    suite('Headers', function() {
      test('constructor copies headers', function() {
        var original = new Headers()
        original.append('Accept', 'application/json')
        original.append('Accept', 'text/plain')
        original.append('Content-Type', 'text/html')

        var headers = new Headers(original)
        assert.equal(headers.get('Accept'), 'application/json, text/plain')
        assert.equal(headers.get('Content-type'), 'text/html')
      })
      test('constructor works with arrays', function() {
        var array = [
          ['Content-Type', 'text/xml'],
          ['Breaking-Bad', '<3']
        ]
        var headers = new Headers(array)

        assert.equal(headers.get('Content-Type'), 'text/xml')
        assert.equal(headers.get('Breaking-Bad'), '<3')
        assert.throws(function() {
          new Headers([
            ['Content-Type'],
          ])
        }, TypeError)
        assert.throws(function() {
          new Headers([
            ['Content-Type', 'a', 'b'],
          ])
        }, TypeError)
      })
      test('headers are case insensitive', function() {
        var headers = new Headers({Accept: 'application/json'})
        assert.equal(headers.get('ACCEPT'), 'application/json')
        assert.equal(headers.get('Accept'), 'application/json')
        assert.equal(headers.get('accept'), 'application/json')
      })
      test('appends to existing', function() {
        var headers = new Headers({Accept: 'application/json'})
        assert.isFalse(headers.has('Content-Type'))
        headers.append('Content-Type', 'application/json')
        assert.isTrue(headers.has('Content-Type'))
        assert.equal(headers.get('Content-Type'), 'application/json')
      })
      test('appends values to existing header name', function() {
        var headers = new Headers({Accept: 'application/json'})
        headers.append('Accept', 'text/plain')
        assert.equal(headers.get('Accept'), 'application/json, text/plain')
      })
      test('sets header name and value', function() {
        var headers = new Headers()
        headers.set('Content-Type', 'application/json')
        assert.equal(headers.get('Content-Type'), 'application/json')
      })
      test('returns null on no header found', function() {
        var headers = new Headers()
        assert.isNull(headers.get('Content-Type'))
      })
      test('has headers that are set', function() {
        var headers = new Headers()
        headers.set('Content-Type', 'application/json')
        assert.isTrue(headers.has('Content-Type'))
      })
      test('deletes headers', function() {
        var headers = new Headers()
        headers.set('Content-Type', 'application/json')
        assert.isTrue(headers.has('Content-Type'))
        headers.delete('Content-Type')
        assert.isFalse(headers.has('Content-Type'))
        assert.isNull(headers.get('Content-Type'))
      })
      test('converts field name to string on set and get', function() {
        var headers = new Headers()
        headers.set(1, 'application/json')
        assert.isTrue(headers.has('1'))
        assert.equal(headers.get(1), 'application/json')
      })
      test('converts field value to string on set and get', function() {
        var headers = new Headers()
        headers.set('Content-Type', 1)
        headers.set('X-CSRF-Token', undefined)
        assert.equal(headers.get('Content-Type'), '1')
        assert.equal(headers.get('X-CSRF-Token'), 'undefined')
      })
      test('throws TypeError on invalid character in field name', function() {
        assert.throws(function() {
          new Headers({'[Accept]': 'application/json'})
        }, TypeError)
        assert.throws(function() {
          new Headers({'Accept:': 'application/json'})
        }, TypeError)
        assert.throws(function() {
          var headers = new Headers()
          headers.set({field: 'value'}, 'application/json')
        }, TypeError)
        assert.throws(function() {
          new Headers({'': 'application/json'})
        }, TypeError)
      })
      featureDependent(test, !brokenFF, 'is iterable with forEach', function() {
        var headers = new Headers()
        headers.append('Accept', 'application/json')
        headers.append('Accept', 'text/plain')
        headers.append('Content-Type', 'text/html')

        var results = []
        headers.forEach(function(value, key, object) {
          results.push({value: value, key: key, object: object})
        })

        assert.equal(results.length, 2)
        assert.deepEqual({key: 'accept', value: 'application/json, text/plain', object: headers}, results[0])
        assert.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1])
      })
      test('forEach accepts second thisArg argument', function() {
        var headers = new Headers({Accept: 'application/json'})
        var thisArg = 42
        headers.forEach(function() {
          assert.equal(this, thisArg)
        }, thisArg)
      })
      featureDependent(test, !brokenFF, 'is iterable with keys', function() {
        var headers = new Headers()
        headers.append('Accept', 'application/json')
        headers.append('Accept', 'text/plain')
        headers.append('Content-Type', 'text/html')

        var iterator = headers.keys()
        assert.deepEqual({done: false, value: 'accept'}, iterator.next())
        assert.deepEqual({done: false, value: 'content-type'}, iterator.next())
        assert.deepEqual({done: true, value: undefined}, iterator.next())
      })
      featureDependent(test, !brokenFF, 'is iterable with values', function() {
        var headers = new Headers()
        headers.append('Accept', 'application/json')
        headers.append('Accept', 'text/plain')
        headers.append('Content-Type', 'text/html')

        var iterator = headers.values()
        assert.deepEqual({done: false, value: 'application/json, text/plain'}, iterator.next())
        assert.deepEqual({done: false, value: 'text/html'}, iterator.next())
        assert.deepEqual({done: true, value: undefined}, iterator.next())
      })
      featureDependent(test, !brokenFF, 'is iterable with entries', function() {
        var headers = new Headers()
        headers.append('Accept', 'application/json')
        headers.append('Accept', 'text/plain')
        headers.append('Content-Type', 'text/html')

        var iterator = headers.entries()
        assert.deepEqual({done: false, value: ['accept', 'application/json, text/plain']}, iterator.next())
        assert.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next())
        assert.deepEqual({done: true, value: undefined}, iterator.next())
      })
    })

    // https://fetch.spec.whatwg.org/#request-class
    suite('Request', function() {
      test('called as normal function', function() {
        assert.throws(function() {
          Request('https://fetch.spec.whatwg.org/')
        })
      })
      test('construct with string url', function() {
        var request = new Request('https://fetch.spec.whatwg.org/')
        assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
      })

      featureDependent(test, support.url, 'construct with URL instance', function() {
        var url = new URL('https://fetch.spec.whatwg.org/')
        url.pathname = 'cors'
        var request = new Request(url)
        assert.equal(request.url, 'https://fetch.spec.whatwg.org/cors')
      })

      test('construct with non-Request object', function() {
        var url = {
          toString: function() {
            return 'https://fetch.spec.whatwg.org/'
          }
        }
        var request = new Request(url)
        assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
      })

      test('construct with Request', function() {
        var request1 = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out',
          headers: {
            accept: 'application/json',
            'Content-Type': 'text/plain'
          }
        })
        var request2 = new Request(request1)

        return request2.text().then(function(body2) {
          assert.equal(body2, 'I work out')
          assert.equal(request2.method, 'POST')
          assert.equal(request2.url, 'https://fetch.spec.whatwg.org/')
          assert.equal(request2.headers.get('accept'), 'application/json')
          assert.equal(request2.headers.get('content-type'), 'text/plain')

          return request1.text().then(
            function() {
              assert(false, 'original request body should have been consumed')
            },
            function(error) {
              assert(error instanceof TypeError, 'expected TypeError for already read body')
            }
          )
        })
      })

      test('construct with Request and override headers', function() {
        var request1 = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out',
          headers: {
            accept: 'application/json',
            'X-Request-ID': '123'
          }
        })
        var request2 = new Request(request1, {
          headers: {'x-test': '42'}
        })

        assert.equal(request2.headers.get('accept'), undefined)
        assert.equal(request2.headers.get('x-request-id'), undefined)
        assert.equal(request2.headers.get('x-test'), '42')
      })

      test('construct with Request and override body', function() {
        var request1 = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out',
          headers: {
            'Content-Type': 'text/plain'
          }
        })
        var request2 = new Request(request1, {
          body: '{"wiggles": 5}',
          headers: {'Content-Type': 'application/json'}
        })

        return request2.json().then(function(data) {
          assert.equal(data.wiggles, 5)
          assert.equal(request2.headers.get('content-type'), 'application/json')
        })
      })

      featureDependent(test, !nativeChrome, 'construct with used Request body', function() {
        var request1 = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out'
        })

        return request1.text().then(function() {
          assert.throws(function() {
            new Request(request1)
          }, TypeError)
        })
      })

      test('GET should not have implicit Content-Type', function() {
        var req = new Request('https://fetch.spec.whatwg.org/')
        assert.equal(req.headers.get('content-type'), undefined)
      })

      test('POST with blank body should not have implicit Content-Type', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post'
        })
        assert.equal(req.headers.get('content-type'), undefined)
      })

      test('construct with string body sets Content-Type header', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out'
        })

        assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
      })

      featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: new Blob(['test'], {type: 'image/png'})
        })

        assert.equal(req.headers.get('content-type'), 'image/png')
      })

      test('construct with body and explicit header uses header', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          headers: {'Content-Type': 'image/png'},
          body: 'I work out'
        })

        assert.equal(req.headers.get('content-type'), 'image/png')
      })

      featureDependent(test, support.blob, 'construct with Blob body and explicit Content-Type header', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          headers: {'Content-Type': 'image/png'},
          body: new Blob(['test'], {type: 'text/plain'})
        })

        assert.equal(req.headers.get('content-type'), 'image/png')
      })

      featureDependent(test, !IEorEdge, 'construct with URLSearchParams body sets Content-Type header', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: new URLSearchParams('a=1&b=2')
        })

        assert.equal(req.headers.get('content-type'), 'application/x-www-form-urlencoded;charset=UTF-8')
      })

      featureDependent(
        test,
        !IEorEdge,
        'construct with URLSearchParams body and explicit Content-Type header',
        function() {
          var req = new Request('https://fetch.spec.whatwg.org/', {
            method: 'post',
            headers: {'Content-Type': 'image/png'},
            body: new URLSearchParams('a=1&b=2')
          })

          assert.equal(req.headers.get('content-type'), 'image/png')
        }
      )

      test('construct with unsupported body type', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: {}
        })

        assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
        return req.text().then(function(bodyText) {
          assert.equal(bodyText, '[object Object]')
        })
      })

      test('construct with null body', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post'
        })

        assert.isNull(req.headers.get('content-type'))
        return req.text().then(function(bodyText) {
          assert.equal(bodyText, '')
        })
      })

      test('clone GET request', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          headers: {'content-type': 'text/plain'}
        })
        var clone = req.clone()

        assert.equal(clone.url, req.url)
        assert.equal(clone.method, 'GET')
        assert.equal(clone.headers.get('content-type'), 'text/plain')
        assert.notEqual(clone.headers, req.headers)
        assert.isFalse(req.bodyUsed)
      })

      test('clone POST request', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          headers: {'content-type': 'text/plain'},
          body: 'I work out'
        })
        var clone = req.clone()

        assert.equal(clone.method, 'POST')
        assert.equal(clone.headers.get('content-type'), 'text/plain')
        assert.notEqual(clone.headers, req.headers)
        assert.equal(req.bodyUsed, false)

        return Promise.all([clone.text(), req.clone().text()]).then(function(bodies) {
          assert.deepEqual(bodies, ['I work out', 'I work out'])
        })
      })

      featureDependent(test, !nativeChrome, 'clone with used Request body', function() {
        var req = new Request('https://fetch.spec.whatwg.org/', {
          method: 'post',
          body: 'I work out'
        })

        return req.text().then(function() {
          assert.throws(function() {
            req.clone()
          }, TypeError)
        })
      })

      testBodyExtract(function(body) {
        return new Request('', {method: 'POST', body: body})
      })

      featureDependent(test, !omitSafari, 'credentials defaults to same-origin', function() {
        var request = new Request('')
        assert.equal(request.credentials, 'same-origin')
      })

      test('credentials is overridable', function() {
        var request = new Request('', {credentials: 'omit'})
        assert.equal(request.credentials, 'omit')
      })
    })

    // https://fetch.spec.whatwg.org/#response-class
    suite('Response', function() {
      featureDependent(test, emptyDefaultStatusText, 'default status is 200', function() {
        var res = new Response()
        assert.equal(res.status, 200)
        assert.equal(res.statusText, '')
        assert.isTrue(res.ok)
      })

      featureDependent(
        test,
        emptyDefaultStatusText,
        'default status is 200 when an explicit undefined status code is passed',
        function() {
          var res = new Response('', {status: undefined})
          assert.equal(res.status, 200)
          assert.equal(res.statusText, '')
          assert.isTrue(res.ok)
        }
      )

      testBodyExtract(function(body) {
        return new Response(body)
      })

      test('called as normal function', function() {
        assert.throws(function() {
          Response('{"foo":"bar"}', {headers: {'content-type': 'application/json'}})
        })
      })
      test('status outside inclusive range 200-599 ', function() {
        assert.throws(function() {
          new Response('', {status: 199})
        })
        for (var i = 0; i < 200; i++) {
          assert.throws(function() {
            new Response('', {status: i})
          })
        }
        for (i = 999; i > 599; i--) {
          assert.throws(function() {
            new Response('', {status: i})
          })
        }
        // A fetch with the url of a `file://` scheme may have a status 0 or
        // similar in some operating systems. In the event that a status is found outside 
        // the standard range of 200-599, and the url start with `file://`
        // the status should return 200
        assert.doesNotThrow(function() {
          new Request('', {status: 0, request: {url: 'file://path/to/local/file'}})
        })
      })

      test('creates Headers object from raw headers', function() {
        var r = new Response('{"foo":"bar"}', {headers: {'content-type': 'application/json'}})
        assert.equal(r.headers instanceof Headers, true)
        return r.json().then(function(json) {
          assert.equal(json.foo, 'bar')
          return json
        })
      })

      test('always creates a new Headers instance', function() {
        var headers = new Headers({'x-hello': 'world'})
        var res = new Response('', {headers: headers})

        assert.equal(res.headers.get('x-hello'), 'world')
        assert.notEqual(res.headers, headers)
      })

      test('clone text response', function() {
        var res = new Response('{"foo":"bar"}', {
          headers: {'content-type': 'application/json'}
        })
        var clone = res.clone()

        assert.notEqual(clone.headers, res.headers, 'headers were cloned')
        assert.equal(clone.headers.get('content-type'), 'application/json')

        return Promise.all([clone.json(), res.json()]).then(function(jsons) {
          assert.deepEqual(jsons[0], jsons[1], 'json of cloned object is the same as original')
        })
      })

      featureDependent(test, support.blob, 'clone blob response', function() {
        var req = new Request(new Blob(['test']))
        req.clone()
        assert.equal(req.bodyUsed, false)
      })

      test('error creates error Response', function() {
        var r = Response.error()
        assert(r instanceof Response)
        assert.equal(r.ok, false)
        assert.equal(r.status, 0)
        assert.equal(r.statusText, '')
        assert.equal(r.type, 'error')
      })

      test('redirect creates redirect Response', function() {
        var r = Response.redirect('https://fetch.spec.whatwg.org/', 301)
        assert(r instanceof Response)
        assert.equal(r.status, 301)
        assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/')
      })

      test('construct with string body sets Content-Type header', function() {
        var r = new Response('I work out')
        assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
      })

      featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
        var r = new Response(new Blob(['test'], {type: 'text/plain'}))
        assert.equal(r.headers.get('content-type'), 'text/plain')
      })

      test('construct with body and explicit header uses header', function() {
        var r = new Response('I work out', {
          headers: {
            'Content-Type': 'text/plain'
          }
        })

        assert.equal(r.headers.get('content-type'), 'text/plain')
      })

      test('construct with undefined statusText', function() {
        var r = new Response('', {statusText: undefined})
        assert.equal(r.statusText, '')
      })

      test('construct with null statusText', function() {
        var r = new Response('', {statusText: null})
        assert.equal(r.statusText, 'null')
      })

      test('init object as first argument', function() {
        var r = new Response({
          status: 201,
          headers: {
            'Content-Type': 'text/html'
          }
        })

        assert.equal(r.status, 200)
        assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
        return r.text().then(function(bodyText) {
          assert.equal(bodyText, '[object Object]')
        })
      })

      test('null as first argument', function() {
        var r = new Response(null)

        assert.isNull(r.headers.get('content-type'))
        return r.text().then(function(bodyText) {
          assert.equal(bodyText, '')
        })
      })
    })

    // https://fetch.spec.whatwg.org/#body-mixin
    suite('Body mixin', function() {
      featureDependent(suite, support.blob, 'arrayBuffer', function() {
        test('resolves arrayBuffer promise', function() {
          return fetch('/hello')
            .then(function(response) {
              return response.arrayBuffer()
            })
            .then(function(buf) {
              assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
              assert.equal(buf.byteLength, 2)
            })
        })

        test('arrayBuffer handles binary data', function() {
          return fetch('/binary')
            .then(function(response) {
              return response.arrayBuffer()
            })
            .then(function(buf) {
              assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
              assert.equal(buf.byteLength, 256, 'buf.byteLength is correct')
              var view = new Uint8Array(buf)
              for (var i = 0; i < 256; i++) {
                assert.equal(view[i], i)
              }
            })
        })

        test('arrayBuffer handles utf-8 data', function() {
          return fetch('/hello/utf8')
            .then(function(response) {
              return response.arrayBuffer()
            })
            .then(function(buf) {
              assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
              assert.equal(buf.byteLength, 5, 'buf.byteLength is correct')
              var octets = Array.prototype.slice.call(new Uint8Array(buf))
              assert.deepEqual(octets, [104, 101, 108, 108, 111])
            })
        })

        test('arrayBuffer handles utf-16le data', function() {
          return fetch('/hello/utf16le')
            .then(function(response) {
              return response.arrayBuffer()
            })
            .then(function(buf) {
              assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
              assert.equal(buf.byteLength, 10, 'buf.byteLength is correct')
              var octets = Array.prototype.slice.call(new Uint8Array(buf))
              assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
            })
        })

        test('rejects arrayBuffer promise after body is consumed', function() {
          return fetch('/hello')
            .then(function(response) {
              assert.equal(response.bodyUsed, false)
              response.blob()
              assert.equal(response.bodyUsed, true)
              return response.arrayBuffer()
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Promise rejected after body consumed')
            })
        })
      })

      featureDependent(suite, support.blob, 'blob', function() {
        test('resolves blob promise', function() {
          return fetch('/hello')
            .then(function(response) {
              return response.blob()
            })
            .then(function(blob) {
              assert(blob instanceof Blob, 'blob is a Blob instance')
              assert.equal(blob.size, 2)
            })
        })

        test('blob handles binary data', function() {
          return fetch('/binary')
            .then(function(response) {
              return response.blob()
            })
            .then(function(blob) {
              assert(blob instanceof Blob, 'blob is a Blob instance')
              assert.equal(blob.size, 256, 'blob.size is correct')
            })
        })

        test('blob handles utf-8 data', function() {
          return fetch('/hello/utf8')
            .then(function(response) {
              return response.blob()
            })
            .then(readBlobAsBytes)
            .then(function(octets) {
              assert.equal(octets.length, 5, 'blob.size is correct')
              assert.deepEqual(octets, [104, 101, 108, 108, 111])
            })
        })

        test('blob handles utf-16le data', function() {
          return fetch('/hello/utf16le')
            .then(function(response) {
              return response.blob()
            })
            .then(readBlobAsBytes)
            .then(function(octets) {
              assert.equal(octets.length, 10, 'blob.size is correct')
              assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
            })
        })

        test('rejects blob promise after body is consumed', function() {
          return fetch('/hello')
            .then(function(response) {
              assert(response.blob, 'Body does not implement blob')
              assert.equal(response.bodyUsed, false)
              response.text()
              assert.equal(response.bodyUsed, true)
              return response.blob()
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Promise rejected after body consumed')
            })
        })
      })

      featureDependent(suite, support.formData, 'formData', function() {
        test('post sets content-type header', function() {
          return fetch('/request', {
            method: 'post',
            body: new FormData()
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(json) {
              assert.equal(json.method, 'POST')
              assert(/^multipart\/form-data;/.test(json.headers['content-type']))
            })
        })

        featureDependent(test, !nativeChrome && !nativeEdge, 'formData rejects after body was consumed', function() {
          return fetch('/json')
            .then(function(response) {
              assert(response.formData, 'Body does not implement formData')
              response.formData()
              return response.formData()
            })
            .catch(function(error) {
              if (error instanceof chai.AssertionError) {
                throw error
              } else {
                assert(error instanceof TypeError, 'Promise rejected after body consumed')
              }
            })
        })

        featureDependent(
          test,
          !nativeChrome && !nativeSafari && !nativeEdge,
          'parses form encoded response',
          function() {
            return fetch('/form')
              .then(function(response) {
                return response.formData()
              })
              .then(function(form) {
                assert(form instanceof FormData, 'Parsed a FormData object')
              })
          }
        )
      })

      suite('json', function() {
        test('parses json response', function() {
          return fetch('/json')
            .then(function(response) {
              return response.json()
            })
            .then(function(json) {
              assert.equal(json.name, 'Hubot')
              assert.equal(json.login, 'hubot')
            })
        })

        test('rejects json promise after body is consumed', function() {
          return fetch('/json')
            .then(function(response) {
              assert(response.json, 'Body does not implement json')
              assert.equal(response.bodyUsed, false)
              response.text()
              assert.equal(response.bodyUsed, true)
              return response.json()
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Promise rejected after body consumed')
            })
        })

        featureDependent(test, !polyfillFirefox, 'handles json parse error', function() {
          return fetch('/json-error')
            .then(function(response) {
              return response.json()
            })
            .catch(function(error) {
              if (!IEorEdge) assert(error instanceof Error, 'JSON exception is an Error instance')
              assert(error.message, 'JSON exception has an error message')
            })
        })
      })

      suite('text', function() {
        test('handles 204 No Content response', function() {
          return fetch('/empty')
            .then(function(response) {
              assert.equal(response.status, 204)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, '')
            })
        })

        test('resolves text promise', function() {
          return fetch('/hello')
            .then(function(response) {
              return response.text()
            })
            .then(function(text) {
              assert.equal(text, 'hi')
            })
        })

        test('rejects text promise after body is consumed', function() {
          return fetch('/hello')
            .then(function(response) {
              assert(response.text, 'Body does not implement text')
              assert.equal(response.bodyUsed, false)
              response.text()
              assert.equal(response.bodyUsed, true)
              return response.text()
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Promise rejected after body consumed')
            })
        })

        test('does not set bodyUsed to true if no body supplied', function() {
          var response = new Response();
          assert(response.text, 'Body does not implement text')
          assert.equal(response.bodyUsed, false)
          response.text()
          assert.equal(response.bodyUsed, false)
          return response.text()
        })
      })
    })

    suite('fetch method', function() {
      suite('promise resolution', function() {
        test('resolves promise on 500 error', function() {
          return fetch('/boom')
            .then(function(response) {
              assert.equal(response.status, 500)
              assert.equal(response.ok, false)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'boom')
            })
        })

        test.skip('rejects promise for network error', function() {
          return fetch('/error')
            .then(function(response) {
              assert(false, 'HTTP status ' + response.status + ' was treated as success')
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Rejected with Error')
            })
        })

        test('rejects when Request constructor throws', function() {
          return fetch('/request', {method: 'GET', body: 'invalid'})
            .then(function() {
              assert(false, 'Invalid Request init was accepted')
            })
            .catch(function(error) {
              assert(error instanceof TypeError, 'Rejected with Error')
            })
        })
      })

      suite('request', function() {
        test('sends headers', function() {
          return fetch('/request', {
            headers: {
              Accept: 'application/json',
              'X-Test': '42'
            }
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(json) {
              assert.equal(json.headers['accept'], 'application/json')
              assert.equal(json.headers['x-test'], '42')
            })
        })

        test('with Request as argument', function() {
          var request = new Request('/request', {
            headers: {
              Accept: 'application/json',
              'X-Test': '42'
            }
          })

          return fetch(request)
            .then(function(response) {
              return response.json()
            })
            .then(function(json) {
              assert.equal(json.headers['accept'], 'application/json')
              assert.equal(json.headers['x-test'], '42')
            })
        })

        test('reusing same Request multiple times', function() {
          var request = new Request('/request', {
            headers: {
              Accept: 'application/json',
              'X-Test': '42'
            }
          })

          var responses = []

          return fetch(request)
            .then(function(response) {
              responses.push(response)
              return fetch(request)
            })
            .then(function(response) {
              responses.push(response)
              return fetch(request)
            })
            .then(function(response) {
              responses.push(response)
              return Promise.all(
                responses.map(function(r) {
                  return r.json()
                })
              )
            })
            .then(function(jsons) {
              jsons.forEach(function(json) {
                assert.equal(json.headers['accept'], 'application/json')
                assert.equal(json.headers['x-test'], '42')
              })
            })
        })

        featureDependent(suite, support.arrayBuffer, 'ArrayBuffer', function() {
          test('ArrayBuffer body', function() {
            return fetch('/request', {
              method: 'post',
              body: arrayBufferFromText('name=Hubot')
            })
              .then(function(response) {
                return response.json()
              })
              .then(function(request) {
                assert.equal(request.method, 'POST')
                assert.equal(request.data, 'name=Hubot')
              })
          })

          test('DataView body', function() {
            return fetch('/request', {
              method: 'post',
              body: new DataView(arrayBufferFromText('name=Hubot'))
            })
              .then(function(response) {
                return response.json()
              })
              .then(function(request) {
                assert.equal(request.method, 'POST')
                assert.equal(request.data, 'name=Hubot')
              })
          })

          test('TypedArray body', function() {
            return fetch('/request', {
              method: 'post',
              body: new Uint8Array(arrayBufferFromText('name=Hubot'))
            })
              .then(function(response) {
                return response.json()
              })
              .then(function(request) {
                assert.equal(request.method, 'POST')
                assert.equal(request.data, 'name=Hubot')
              })
          })
        })

        featureDependent(test, !IEorEdge, 'sends URLSearchParams body', function() {
          return fetch('/request', {
            method: 'post',
            body: new URLSearchParams('a=1&b=2')
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'POST')
              assert.equal(request.data, 'a=1&b=2')
            })
        })
      })

      featureDependent(suite, exerciseMode !== 'native' || support.aborting, 'aborting', function() {
        test('Request init creates an AbortSignal without option', function() {
          var request = new Request('/request')
          assert.ok(request.signal);
          assert.equal(request.signal.aborted, false);
        })

        test('Request init passes AbortSignal from option', function () {
          var controller = new AbortController()
          var request = new Request('/request', {signal: controller.signal})
          assert.ok(request.signal);
          assert.deepEqual(controller.signal, request.signal);
        })

        test('initially aborted signal', function () {
          var controller = new AbortController()
          controller.abort()

          return fetch('/request', {
            signal: controller.signal
          }).then(
            function() {
              assert.ok(false)
            },
            function(error) {
              if (!IEorEdge) assert.instanceOf(error, WHATWGFetch.DOMException)
              assert.equal(error.name, 'AbortError')
            }
          )
        })

        test('initially aborted signal within Request', function() {
          var controller = new AbortController()
          controller.abort()

          var request = new Request('/request', {signal: controller.signal})

          return fetch(request).then(
            function() {
              assert.ok(false)
            },
            function(error) {
              assert.equal(error.name, 'AbortError')
            }
          )
        })

        test('mid-request', function() {
          var controller = new AbortController()

          setTimeout(function() {
            controller.abort()
          }, 30)

          return fetch('/slow?_=' + new Date().getTime(), {
            signal: controller.signal
          }).then(
            function() {
              assert.ok(false)
            },
            function(error) {
              assert.equal(error.name, 'AbortError')
            }
          )
        })

        test('mid-request within Request', function() {
          var controller = new AbortController()
          var request = new Request('/slow?_=' + new Date().getTime(), {signal: controller.signal})

          setTimeout(function() {
            controller.abort()
          }, 30)

          return fetch(request).then(
            function() {
              assert.ok(false)
            },
            function(error) {
              assert.equal(error.name, 'AbortError')
            }
          )
        })

        test('abort multiple with same signal', function() {
          var controller = new AbortController()

          setTimeout(function() {
            controller.abort()
          }, 30)

          return Promise.all([
            fetch('/slow?_=' + new Date().getTime(), {
              signal: controller.signal
            }).then(
              function() {
                assert.ok(false)
              },
              function(error) {
                assert.equal(error.name, 'AbortError')
              }
            ),
            fetch('/slow?_=' + new Date().getTime(), {
              signal: controller.signal
            }).then(
              function() {
                assert.ok(false)
              },
              function(error) {
                assert.equal(error.name, 'AbortError')
              }
            )
          ])
        })
      })

      suite('response', function() {
        test('populates body', function() {
          return fetch('/hello')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })

        test('parses headers', function() {
          return fetch('/headers?' + new Date().getTime()).then(function(response) {
            assert.equal(response.headers.get('Date'), 'Mon, 13 Oct 2014 21:02:27 GMT')
            assert.equal(response.headers.get('Content-Type'), 'text/html; charset=utf-8')
          })
        })
      })

      // https://fetch.spec.whatwg.org/#methods
      suite('HTTP methods', function() {
        test('supports HTTP GET', function() {
          return fetch('/request', {
            method: 'get'
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'GET')
              assert.equal(request.data, '')
            })
        })

        test('GET with body throws TypeError', function() {
          assert.throw(function() {
            new Request('', {
              method: 'get',
              body: 'invalid'
            })
          }, TypeError)
        })

        test('HEAD with body throws TypeError', function() {
          assert.throw(function() {
            new Request('', {
              method: 'head',
              body: 'invalid'
            })
          }, TypeError)
        })

        test('supports HTTP POST', function() {
          return fetch('/request', {
            method: 'post',
            body: 'name=Hubot'
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'POST')
              assert.equal(request.data, 'name=Hubot')
            })
        })

        test('supports HTTP PUT', function() {
          return fetch('/request', {
            method: 'put',
            body: 'name=Hubot'
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'PUT')
              assert.equal(request.data, 'name=Hubot')
            })
        })

        test('supports HTTP PATCH', function() {
          return fetch('/request', {
            method: 'PATCH',
            body: 'name=Hubot'
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'PATCH')
              assert.equal(request.data, 'name=Hubot')
            })
        })

        test('supports HTTP DELETE', function() {
          return fetch('/request', {
            method: 'delete'
          })
            .then(function(response) {
              return response.json()
            })
            .then(function(request) {
              assert.equal(request.method, 'DELETE')
              assert.equal(request.data, '')
            })
        })
      })

      // https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
      suite('Atomic HTTP redirect handling', function() {
        test('handles 301 redirect response', function() {
          return fetch('/redirect/301')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              assert.match(response.url, /\/hello/)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })

        test('handles 302 redirect response', function() {
          return fetch('/redirect/302')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              assert.match(response.url, /\/hello/)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })

        test('handles 303 redirect response', function() {
          return fetch('/redirect/303')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              assert.match(response.url, /\/hello/)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })

        test('handles 307 redirect response', function() {
          return fetch('/redirect/307')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              assert.match(response.url, /\/hello/)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })

        featureDependent(test, support.permanentRedirect, 'handles 308 redirect response', function() {
          return fetch('/redirect/308')
            .then(function(response) {
              assert.equal(response.status, 200)
              assert.equal(response.ok, true)
              assert.match(response.url, /\/hello/)
              return response.text()
            })
            .then(function(body) {
              assert.equal(body, 'hi')
            })
        })
      })

      // https://fetch.spec.whatwg.org/#concept-request-credentials-mode
      suite('credentials mode', function() {
        setup(function() {
          return fetch('/cookie?name=foo&value=reset', {credentials: 'same-origin'})
        })

        featureDependent(suite, exerciseMode === 'native', 'omit', function() {
          test('does not accept cookies with omit credentials', function() {
            return fetch('/cookie?name=foo&value=bar', {credentials: 'omit'})
              .then(function() {
                return fetch('/cookie?name=foo', {credentials: 'same-origin'})
              })
              .then(function(response) {
                return response.text()
              })
              .then(function(data) {
                assert.equal(data, 'reset')
              })
          })

          test('does not send cookies with omit credentials', function() {
            return fetch('/cookie?name=foo&value=bar')
              .then(function() {
                return fetch('/cookie?name=foo', {credentials: 'omit'})
              })
              .then(function(response) {
                return response.text()
              })
              .then(function(data) {
                assert.equal(data, '')
              })
          })
        })

        suite('same-origin', function() {
          test('send cookies with same-origin credentials', function() {
            return fetch('/cookie?name=foo&value=bar', {credentials: 'same-origin'})
              .then(function() {
                return fetch('/cookie?name=foo', {credentials: 'same-origin'})
              })
              .then(function(response) {
                return response.text()
              })
              .then(function(data) {
                assert.equal(data, 'bar')
              })
          })
        })

        suite('include', function() {
          test('send cookies with include credentials', function() {
            return fetch('/cookie?name=foo&value=bar', {credentials: 'include'})
              .then(function() {
                return fetch('/cookie?name=foo', {credentials: 'include'})
              })
              .then(function(response) {
                return response.text()
              })
              .then(function(data) {
                assert.equal(data, 'bar')
              })
          })
        })
      })
    })
  })
})


================================================
FILE: test/worker-adapter.js
================================================
/* eslint-env mocha */
/* globals Mocha */
var mochaRun = mocha.run
mocha.run = function() {}

mocha.suite.suites.unshift(Mocha.Suite.create(mocha.suite, 'worker'))

var worker = new Worker('/base/test/worker.js')

worker.addEventListener('message', function(e) {
  switch (e.data.name) {
    case 'pass':
      test(e.data.title, function() {})
      break
    case 'pending':
      test(e.data.title)
      break
    case 'fail':
      test(e.data.title, function() {
        var err = new Error(e.data.message)
        err.stack = e.data.stack
        throw err
      })
      break
    case 'end':
      mochaRun()
      break
  }
})


================================================
FILE: test/worker.js
================================================
/* eslint-env worker */
/* globals mocha chai */
importScripts('/base/node_modules/mocha/mocha.js')
importScripts('/base/node_modules/chai/chai.js')

mocha.setup('tdd')
self.assert = chai.assert

importScripts('/base/node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js')
importScripts('/base/dist/fetch.umd.js')
importScripts('/base/test/test.js')

function title(test) {
  return test.fullTitle().replace(/#/g, '')
}

function reporter(runner) {
  runner.on('pending', function(test) {
    self.postMessage({name: 'pending', title: title(test)})
  })

  runner.on('pass', function(test) {
    self.postMessage({name: 'pass', title: title(test)})
  })

  runner.on('fail', function(test, err) {
    self.postMessage({
      name: 'fail',
      title: title(test),
      message: err.message,
      stack: err.stack
    })
  })

  runner.on('end', function() {
    self.postMessage({name: 'end'})
  })
}

mocha.reporter(reporter).run()
Download .txt
gitextract_sunlppts/

├── .eslintignore
├── .eslintrc.js
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── lock.yml
│       ├── node.js.yml
│       ├── release.yml
│       └── scorecard.yml
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── bower.json
├── fetch.js
├── fetch.js.flow
├── package.json
├── prettier.config.js
├── rollup.config.js
└── test/
    ├── karma-worker.config.js
    ├── karma.config.js
    ├── server.js
    ├── test.js
    ├── worker-adapter.js
    └── worker.js
Download .txt
SYMBOL INDEX (26 symbols across 3 files)

FILE: fetch.js
  function isDataView (line 27) | function isDataView(obj) {
  function normalizeName (line 51) | function normalizeName(name) {
  function normalizeValue (line 61) | function normalizeValue(value) {
  function iteratorFor (line 69) | function iteratorFor(items) {
  function Headers (line 86) | function Headers(headers) {
  function consumed (line 167) | function consumed(body) {
  function fileReaderReady (line 175) | function fileReaderReady(reader) {
  function readBlobAsArrayBuffer (line 186) | function readBlobAsArrayBuffer(blob) {
  function readBlobAsText (line 193) | function readBlobAsText(blob) {
  function readArrayBufferAsText (line 202) | function readArrayBufferAsText(buf) {
  function bufferClone (line 212) | function bufferClone(buf) {
  function Body (line 222) | function Body() {
  function normalizeMethod (line 345) | function normalizeMethod(method) {
  function Request (line 350) | function Request(input, options) {
  function decode (line 417) | function decode(body) {
  function parseHeaders (line 433) | function parseHeaders(rawHeaders) {
  function Response (line 463) | function Response(bodyInit, options) {
  function fetch (line 526) | function fetch(input, init) {

FILE: test/test.js
  function readBlobAsText (line 32) | function readBlobAsText(blob) {
  function readBlobAsBytes (line 51) | function readBlobAsBytes(blob) {
  function arrayBufferFromText (line 71) | function arrayBufferFromText(text) {
  function readArrayBufferAsText (line 81) | function readArrayBufferAsText(buf) {
  function featureDependent (line 108) | function featureDependent(testOrSuite, condition) {
  function testBodyExtract (line 134) | function testBodyExtract(factory) {

FILE: test/worker.js
  function title (line 13) | function title(test) {
  function reporter (line 17) | function reporter(runner) {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (149K chars).
[
  {
    "path": ".eslintignore",
    "chars": 13,
    "preview": "node_modules\n"
  },
  {
    "path": ".eslintrc.js",
    "chars": 258,
    "preview": "/* eslint-env node */\nmodule.exports = {\n    \"env\": {\n        \"browser\": true,\n        \"es2021\": true\n    },\n    \"extend"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 211,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n    group"
  },
  {
    "path": ".github/workflows/lock.yml",
    "chars": 381,
    "preview": "name: 'Lock threads'\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n\npermissions: {}\n\njobs:\n  lock:\n    runs-on: ubuntu-latest"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "chars": 850,
    "preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 376,
    "preview": "on:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: write\n  pull-requests: write\n\nname: release-please\n\njob"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "chars": 2215,
    "preview": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by "
  },
  {
    "path": ".gitignore",
    "chars": 94,
    "preview": ".env\npackage-lock.json\ndist/\nbower_components/\nnode_modules/\nsauce_connect/\nsauce_connect.log\n"
  },
  {
    "path": ".npmignore",
    "chars": 88,
    "preview": ".env\npackage-lock.json\nbower_components/\nnode_modules/\nsauce_connect/\nsauce_connect.log\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 36838,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file. Dates are displayed in UTC.\n\n## [3.6.2"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3224,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2345,
    "preview": "# Contributing\n\nThank you for your interest in contributing to our `fetch` polyfill!\n\nNote that we only accept features "
  },
  {
    "path": "LICENSE",
    "chars": 1061,
    "preview": "Copyright (c) 2014-2023 GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of th"
  },
  {
    "path": "Makefile",
    "chars": 425,
    "preview": "test: lint dist/fetch.umd.js\n\nlint: node_modules/\n\t./node_modules/.bin/eslint --report-unused-disable-directives *.js te"
  },
  {
    "path": "README.md",
    "chars": 10975,
    "preview": "# window.fetch polyfill\n\n[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/JakeChampion/fetch"
  },
  {
    "path": "SECURITY.md",
    "chars": 689,
    "preview": "# Security Policy\n\n## Supported Versions\n\nSecurity updates are applied only to the latest release.\n\n## Reporting a Vulne"
  },
  {
    "path": "bower.json",
    "chars": 165,
    "preview": "{\n  \"name\": \"fetch\",\n  \"main\": \"fetch.js\",\n  \"ignore\": [\n    \".*\",\n    \"*.md\",\n    \"examples/\",\n    \"Makefile\",\n    \"pac"
  },
  {
    "path": "fetch.js",
    "chars": 18007,
    "preview": "/* eslint-disable no-prototype-builtins */\nvar g =\n  (typeof globalThis !== 'undefined' && globalThis) ||\n  (typeof self"
  },
  {
    "path": "fetch.js.flow",
    "chars": 3160,
    "preview": "/* @flow strict */\n\ntype CredentialsType = 'omit' | 'same-origin' | 'include'\n\ntype ResponseType =  'default' | 'error'\n"
  },
  {
    "path": "package.json",
    "chars": 1249,
    "preview": "{\n  \"name\": \"whatwg-fetch\",\n  \"description\": \"A window.fetch polyfill.\",\n  \"version\": \"3.6.20\",\n  \"main\": \"./dist/fetch."
  },
  {
    "path": "prettier.config.js",
    "chars": 87,
    "preview": "/* eslint-env node */\nmodule.exports = require('eslint-plugin-github/prettier.config')\n"
  },
  {
    "path": "rollup.config.js",
    "chars": 130,
    "preview": "export default {\n  input: 'fetch.js',\n  output: {\n    file: 'dist/fetch.umd.js',\n    format: 'umd',\n    name: 'WHATWGFet"
  },
  {
    "path": "test/karma-worker.config.js",
    "chars": 470,
    "preview": "/* eslint-env node */\nconst parentConfig = require('./karma.config')\n\nmodule.exports = function(config) {\n  parentConfig"
  },
  {
    "path": "test/karma.config.js",
    "chars": 1568,
    "preview": "/* eslint-env node */\nconst serverEndpoints = require('./server')\n\nmodule.exports = function(config) {\n  config.set({\n  "
  },
  {
    "path": "test/server.js",
    "chars": 3461,
    "preview": "/* eslint-env node */\nconst url = require('url')\nconst querystring = require('querystring')\n\nconst routes = {\n  '/reques"
  },
  {
    "path": "test/test.js",
    "chars": 53578,
    "preview": "/* eslint-env mocha */\n/* globals chai assert FileReaderSync assert WHATWGFetch */\nvar IEorEdge = /Edge\\//.test(navigato"
  },
  {
    "path": "test/worker-adapter.js",
    "chars": 638,
    "preview": "/* eslint-env mocha */\n/* globals Mocha */\nvar mochaRun = mocha.run\nmocha.run = function() {}\n\nmocha.suite.suites.unshif"
  },
  {
    "path": "test/worker.js",
    "chars": 959,
    "preview": "/* eslint-env worker */\n/* globals mocha chai */\nimportScripts('/base/node_modules/mocha/mocha.js')\nimportScripts('/base"
  }
]

About this extraction

This page contains the full source code of the JakeChampion/fetch GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (140.2 KB), approximately 37.1k tokens, and a symbol index with 26 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!