Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-fetch

Package Overview
Dependencies
Maintainers
4
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-fetch - npm Package Compare versions

Comparing version 3.0.0-beta.5 to 3.0.0-beta.6

./@types/index.d.ts

2

LICENSE.md
The MIT License (MIT)
Copyright (c) 2016 David Frank
Copyright (c) 2016 - 2020 Node Fetch Team

@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "node-fetch",
"version": "3.0.0-beta.5",
"description": "A light-weight module that brings window.fetch to node.js",
"main": "./dist/index.js",
"module": "./src/index.js",
"sideEffects": false,
"exports": {
"import": "./src/index.js",
"require": "./dist/index.js"
},
"files": [
"src",
"dist",
"*.d.ts"
],
"engines": {
"node": ">=10"
},
"scripts": {
"build": "babel src --out-dir dist",
"test": "nyc --reporter=html --reporter=text mocha --require @babel/register --throw-deprecation",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"lint": "xo"
},
"repository": {
"type": "git",
"url": "https://github.com/node-fetch/node-fetch.git"
},
"keywords": [
"fetch",
"http",
"promise"
],
"author": "David Frank",
"license": "MIT",
"bugs": {
"url": "https://github.com/node-fetch/node-fetch/issues"
},
"homepage": "https://github.com/node-fetch/node-fetch",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/register": "^7.9.0",
"@istanbuljs/nyc-config-babel": "^3.0.0",
"abort-controller": "^3.0.0",
"abortcontroller-polyfill": "^1.4.0",
"babel-plugin-add-module-exports": "^1.0.2",
"babel-plugin-istanbul": "^6.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-iterator": "^3.0.2",
"chai-string": "^1.5.0",
"coveralls": "^3.0.13",
"form-data": "^3.0.0",
"mocha": "^7.1.1",
"nyc": "^15.0.1",
"parted": "^0.1.1",
"promise": "^8.1.0",
"resumer": "0.0.0",
"string-to-arraybuffer": "^1.0.2",
"xo": "^0.29.1"
},
"dependencies": {
"data-uri-to-buffer": "^3.0.0",
"fetch-blob": "^1.0.5"
},
"xo": {
"envs": [
"node",
"browser"
],
"rules": {
"complexity": 0,
"promise/prefer-await-to-then": 0,
"no-mixed-operators": 0,
"no-negated-condition": 0,
"unicorn/prevent-abbreviations": 0,
"@typescript-eslint/prefer-readonly-parameter-types": 0
},
"ignores": [
"name": "node-fetch",
"version": "3.0.0-beta.6",
"description": "A light-weight module that brings window.fetch to node.js",
"main": "./dist/index.cjs",
"module": "./src/index.js",
"sideEffects": false,
"type": "module",
"exports": {
"import": "./src/index.js",
"require": "./dist/index.cjs"
},
"files": [
"src",
"dist",
"@types/index.d.ts"
],
"types": "./@types/index.d.ts",
"engines": {
"node": ">=10.16"
},
"scripts": {
"build": "rollup -c",
"test": "node --experimental-modules node_modules/c8/bin/c8 --reporter=html --reporter=lcov --reporter=text --check-coverage node --experimental-modules node_modules/mocha/bin/mocha",
"coverage": "c8 report --reporter=text-lcov | coveralls",
"test-types": "tsd",
"lint": "xo"
},
"repository": {
"type": "git",
"url": "https://github.com/node-fetch/node-fetch.git"
},
"keywords": [
"fetch",
"http",
"promise"
],
"author": "David Frank",
"license": "MIT",
"bugs": {
"url": "https://github.com/node-fetch/node-fetch/issues"
},
"homepage": "https://github.com/node-fetch/node-fetch",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
},
"devDependencies": {
"abort-controller": "^3.0.0",
"abortcontroller-polyfill": "^1.4.0",
"c8": "^7.1.2",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-iterator": "^3.0.2",
"chai-string": "^1.5.0",
"coveralls": "^3.1.0",
"delay": "^4.3.0",
"form-data": "^3.0.0",
"mocha": "^7.1.2",
"p-timeout": "^3.2.0",
"parted": "^0.1.1",
"promise": "^8.1.0",
"resumer": "0.0.0",
"rollup": "^2.10.8",
"string-to-arraybuffer": "^1.0.2",
"tsc": "^1.20150623.0",
"tsd": "^0.11.0",
"xo": "^0.30.0"
},
"dependencies": {
"data-uri-to-buffer": "^3.0.0",
"fetch-blob": "^1.0.6"
},
"tsd": {
"cwd": "@types",
"compilerOptions": {
"target": "esnext",
"lib": [
"es2018"
],
"allowSyntheticDefaultImports": true
}
},
"xo": {
"envs": [
"node",
"browser"
],
"rules": {
"complexity": 0,
"import/extensions": 0,
"import/no-useless-path-segments": 0,
"unicorn/import-index": 0,
"capitalized-comments": 0
},
"ignores": [
"dist",
"index.d.ts"
],
"overrides": [
{
"files": "test/**/*.js",
"envs": [
"node",
"mocha"
],
"rules": {
"max-nested-callbacks": 0,
"no-unused-expressions": 0,
"new-cap": 0,
"guard-for-in": 0
}
},
{
"files": "example.js",
"rules": {
"import/no-extraneous-dependencies": 0
}
}
]
},
"babel": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": true
}
}
]
],
"plugins": [
"add-module-exports",
"istanbul"
]
},
"nyc": {
"extends": "@istanbuljs/nyc-config-babel"
},
"runkitExampleFilename": "example.js"
"@types"
],
"overrides": [
{
"files": "test/**/*.js",
"envs": [
"node",
"mocha"
],
"rules": {
"max-nested-callbacks": 0,
"no-unused-expressions": 0,
"new-cap": 0,
"guard-for-in": 0,
"unicorn/prevent-abbreviations": 0,
"promise/prefer-await-to-then": 0,
"ava/no-import-test-files": 0
}
},
{
"files": "example.js",
"rules": {
"import/no-extraneous-dependencies": 0
}
}
]
},
"runkitExampleFilename": "example.js"
}

@@ -5,8 +5,8 @@ <div align="center">

<p>A light-weight module that brings <code>window.fetch</code> to Node.js.</p>
<a href="https://travis-ci.com/node-fetch/node-fetch"><img src="https://img.shields.io/travis/com/node-fetch/node-fetch/master?style=flat-square" alt="Build status"></a>
<a href="https://coveralls.io/github/node-fetch/node-fetch"><img src="https://img.shields.io/coveralls/github/node-fetch/node-fetch?style=flat-square" alt="Coverage status"></a>
<a href="https://packagephobia.now.sh/result?p=node-fetch"><img src="https://flat.badgen.net/packagephobia/install/node-fetch" alt="Current version"></a>
<a href="https://www.npmjs.com/package/node-fetch"><img src="https://img.shields.io/npm/v/node-fetch?style=flat-square" alt="Install size"></a>
<a href="https://github.com/sindresorhus/awesome-nodejs"><img src="https://awesome.re/mentioned-badge-flat.svg" alt="Mentioned in Awesome Node.js"></a>
<a href="https://discord.gg/Zxbndcm"><img src="https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord&style=flat-square" alt="Discord"></a>
<a href="https://github.com/node-fetch/node-fetch/actions"><img src="https://github.com/node-fetch/node-fetch/workflows/CI/badge.svg?branch=master" alt="Build status"></a>
<a href="https://coveralls.io/github/node-fetch/node-fetch"><img src="https://img.shields.io/coveralls/github/node-fetch/node-fetch" alt="Coverage status"></a>
<a href="https://packagephobia.now.sh/result?p=node-fetch"><img src="https://badgen.net/packagephobia/install/node-fetch" alt="Current version"></a>
<a href="https://www.npmjs.com/package/node-fetch"><img src="https://img.shields.io/npm/v/node-fetch" alt="Install size"></a>
<a href="https://github.com/sindresorhus/awesome-nodejs"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Node.js"></a>
<a href="https://discord.gg/Zxbndcm"><img src="https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord" alt="Discord"></a>
<br>

@@ -31,46 +31,46 @@ <br>

- [Common Usage](#common-usage)
- [Plain text or HTML](#plain-text-or-html)
- [JSON](#json)
- [Simple Post](#simple-post)
- [Post with JSON](#post-with-json)
- [Post with form parameters](#post-with-form-parameters)
- [Handling exceptions](#handling-exceptions)
- [Handling client and server errors](#handling-client-and-server-errors)
- [Handling cookies](#handling-cookies)
- [Plain text or HTML](#plain-text-or-html)
- [JSON](#json)
- [Simple Post](#simple-post)
- [Post with JSON](#post-with-json)
- [Post with form parameters](#post-with-form-parameters)
- [Handling exceptions](#handling-exceptions)
- [Handling client and server errors](#handling-client-and-server-errors)
- [Handling cookies](#handling-cookies)
- [Advanced Usage](#advanced-usage)
- [Streams](#streams)
- [Buffer](#buffer)
- [Accessing Headers and other Meta data](#accessing-headers-and-other-meta-data)
- [Extract Set-Cookie Header](#extract-set-cookie-header)
- [Post data using a file stream](#post-data-using-a-file-stream)
- [Post with form-data (detect multipart)](#post-with-form-data-detect-multipart)
- [Request cancellation with AbortSignal](#request-cancellation-with-abortsignal)
- [Streams](#streams)
- [Buffer](#buffer)
- [Accessing Headers and other Meta data](#accessing-headers-and-other-meta-data)
- [Extract Set-Cookie Header](#extract-set-cookie-header)
- [Post data using a file stream](#post-data-using-a-file-stream)
- [Post with form-data (detect multipart)](#post-with-form-data-detect-multipart)
- [Request cancellation with AbortSignal](#request-cancellation-with-abortsignal)
- [API](#api)
- [fetch(url[, options])](#fetchurl-options)
- [Options](#options)
- [Default Headers](#default-headers)
- [Custom Agent](#custom-agent)
- [Custom highWaterMark](#custom-highwatermark)
- [Class: Request](#class-request)
- [new Request(input[, options])](#new-requestinput-options)
- [Class: Response](#class-response)
- [new Response([body[, options]])](#new-responsebody-options)
- [response.ok](#responseok)
- [response.redirected](#responseredirected)
- [Class: Headers](#class-headers)
- [new Headers([init])](#new-headersinit)
- [Interface: Body](#interface-body)
- [body.body](#bodybody)
- [body.bodyUsed](#bodybodyused)
- [body.arrayBuffer()](#bodyarraybuffer)
- [body.blob()](#bodyblob)
- [body.json()](#bodyjson)
- [body.text()](#bodytext)
- [body.buffer()](#bodybuffer)
- [Class: FetchError](#class-fetcherror)
- [Class: AbortError](#class-aborterror)
- [fetch(url[, options])](#fetchurl-options)
- [Options](#options)
- [Default Headers](#default-headers)
- [Custom Agent](#custom-agent)
- [Custom highWaterMark](#custom-highwatermark)
- [Class: Request](#class-request)
- [new Request(input[, options])](#new-requestinput-options)
- [Class: Response](#class-response)
- [new Response([body[, options]])](#new-responsebody-options)
- [response.ok](#responseok)
- [response.redirected](#responseredirected)
- [Class: Headers](#class-headers)
- [new Headers([init])](#new-headersinit)
- [Interface: Body](#interface-body)
- [body.body](#bodybody)
- [body.bodyUsed](#bodybodyused)
- [body.arrayBuffer()](#bodyarraybuffer)
- [body.blob()](#bodyblob)
- [body.json()](#bodyjson)
- [body.text()](#bodytext)
- [body.buffer()](#bodybuffer)
- [Class: FetchError](#class-fetcherror)
- [Class: AbortError](#class-aborterror)
- [TypeScript](#typescript)
- [Acknowledgement](#acknowledgement)
- [Team](#team)
- [Former](#former)
- [Former](#former)
- [License](#license)

@@ -93,3 +93,3 @@

- Decode content encoding (gzip/deflate) properly, and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.
- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors][error-handling.md] for troubleshooting.
- Useful extensions such as redirect limit, response size limit, [explicit errors][error-handling.md] for troubleshooting.

@@ -141,18 +141,4 @@ ## Difference from client-side fetch

For versions of node earlier than 12.x, use this `globalThis` [polyfill](https://mathiasbynens.be/notes/globalthis):
For versions of Node earlier than 12, use this `globalThis` [polyfill](https://mathiasbynens.be/notes/globalthis).
```js
(function() {
if (typeof globalThis === 'object') return;
Object.defineProperty(Object.prototype, '__magic__', {
get: function() {
return this;
},
configurable: true
});
__magic__.globalThis = __magic__;
delete Object.prototype.__magic__;
}());
```
## Upgrading

@@ -175,5 +161,8 @@

fetch('https://github.com/')
.then(res => res.text())
.then(body => console.log(body));
(async () => {
const response = await fetch('https://github.com/');
const body = await response.text();
console.log(body);
})();
```

@@ -186,5 +175,8 @@

fetch('https://api.github.com/users/github')
.then(res => res.json())
.then(json => console.log(json));
(async () => {
const response = await fetch('https://api.github.com/users/github');
const json = await response.json();
console.log(json);
})();
```

@@ -197,5 +189,8 @@

fetch('https://httpbin.org/post', {method: 'POST', body: 'a=1'})
.then(res => res.json()) // expecting a json response
.then(json => console.log(json));
(async () => {
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: 'a=1'});
const json = await response.json();
console.log(json);
})();
```

@@ -208,11 +203,14 @@

const body = {a: 1};
(async () => {
const body = {a: 1};
fetch('https://httpbin.org/post', {
method: 'post',
body: JSON.stringify(body),
headers: {'Content-Type': 'application/json'}
})
.then(res => res.json())
.then(json => console.log(json));
const response = await fetch('https://httpbin.org/post', {
method: 'post',
body: JSON.stringify(body),
headers: {'Content-Type': 'application/json'}
});
const json = await response.json();
console.log(json);
})();
```

@@ -232,5 +230,8 @@

fetch('https://httpbin.org/post', {method: 'POST', body: params})
.then(res => res.json())
.then(json => console.log(json));
(async () => {
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: params});
const json = await response.json();
console.log(json);
})();
```

@@ -242,3 +243,3 @@

Adding a catch to the fetch promise chain will catch _all_ exceptions, such as errors originating from node core libraries, like network errors, and operational errors which are instances of FetchError. See the [error handling document][error-handling.md] for more details.
Wrapping the fetch function into a `try/catch` block will catch _all_ exceptions, such as errors originating from node core libraries, like network errors, and operational errors which are instances of FetchError. See the [error handling document][error-handling.md] for more details.

@@ -248,3 +249,7 @@ ```js

fetch('https://domain.invalid/').catch(err => console.error(err));
try {
fetch('https://domain.invalid/');
} catch (error) {
console.log(error);
}
```

@@ -259,3 +264,3 @@

function checkStatus(res) {
const checkStatus = res => {
if (res.ok) {

@@ -269,5 +274,8 @@ // res.status >= 200 && res.status < 300

fetch('https://httpbin.org/status/400')
.then(checkStatus)
.then(res => console.log('will not get here...'));
(async () => {
const response = await fetch('https://httpbin.org/status/400');
const data = checkStatus(response);
console.log(data); //=> MyCustomError
})();
```

@@ -290,10 +298,11 @@

fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
.then(res => {
if (!res.ok) {
throw new Error(`unexpected response ${res.statusText}`);
}
(async () => {
const response = await fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png');
if (response.ok) {
return streamPipeline(res.body, fs.createWriteStream('./octocat.png'));
}
return streamPipeline(res.body, fs.createWriteStream('./octocat.png'));
});
throw new Error(`unexpected response ${res.statusText}`);
})();
```

@@ -309,8 +318,9 @@

fetch('https://octodex.github.com/images/Fintechtocat.png')
.then(res => res.buffer())
.then(buffer => fileType(buffer))
.then(type => {
console.log(type);
});
(async () => {
const response = await fetch('https://octodex.github.com/images/Fintechtocat.png');
const buffer = await response.buffer();
const type = fileType.fromBuffer(buffer)
console.log(type);
})();
```

@@ -323,3 +333,5 @@

fetch('https://github.com/').then(res => {
(async () => {
const response = await fetch('https://github.com/');
console.log(res.ok);

@@ -330,3 +342,3 @@ console.log(res.status);

console.log(res.headers.get('content-type'));
});
})();
```

@@ -341,6 +353,8 @@

fetch('https://example.com').then(res => {
// returns an array of values, instead of a string of comma-separated values
(async () => {
const response = await fetch('https://example.com');
// Returns an array of values, instead of a string of comma-separated values
console.log(res.headers.raw()['set-cookie']);
});
})();
```

@@ -356,5 +370,8 @@

fetch('https://httpbin.org/post', {method: 'POST', body: stream})
.then(res => res.json())
.then(json => console.log(json));
(async () => {
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: stream});
const json = await response.json();
console.log(json)
})();
```

@@ -371,5 +388,8 @@

fetch('https://httpbin.org/post', {method: 'POST', body: form})
.then(res => res.json())
.then(json => console.log(json));
(async () => {
const response = await fetch('https://httpbin.org/post', {method: 'POST', body: form});
const json = await response.json();
console.log(json)
})();

@@ -385,5 +405,8 @@ // OR, using custom headers

fetch('https://httpbin.org/post', options)
.then(res => res.json())
.then(json => console.log(json));
(async () => {
const response = await fetch('https://httpbin.org/post', options);
const json = await response.json();
console.log(json)
})();
```

@@ -406,20 +429,19 @@

fetch('https://example.com', {signal: controller.signal})
.then(res => res.json())
.then(
data => {
useData(data);
},
err => {
if (err.name === 'AbortError') {
console.log('request was aborted');
}
(async () => {
try {
const response = await fetch('https://example.com', {signal: controller.signal});
const data = await response.json();
useData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('request was aborted');
}
)
.finally(() => {
} finally {
clearTimeout(timeout);
});
}
})();
```
See [test cases](https://github.com/node-fetch/node-fetch/blob/master/test/test.js) for more examples.
See [test cases](https://github.com/node-fetch/node-fetch/blob/master/test/) for more examples.

@@ -448,10 +470,9 @@ ## API

method: 'GET',
headers: {}, // request headers. format is the identical to that accepted by the Headers constructor (see below)
body: null, // request body. can be null, a string, a Buffer, a Blob, or a Node.js Readable stream
redirect: 'follow', // set to `manual` to extract redirect headers, `error` to reject redirect
signal: null, // pass an instance of AbortSignal to optionally abort requests
headers: {}, // Request headers. format is the identical to that accepted by the Headers constructor (see below)
body: null, // Request body. can be null, a string, a Buffer, a Blob, or a Node.js Readable stream
redirect: 'follow', // Set to `manual` to extract redirect headers, `error` to reject redirect
signal: null, // Pass an instance of AbortSignal to optionally abort requests
// The following properties are node-fetch extensions
follow: 20, // maximum redirect count. 0 to not follow redirect
timeout: 0, // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies). Signal is recommended instead.
compress: true, // support gzip/deflate content encoding. false to disable

@@ -468,11 +489,12 @@ size: 0, // maximum response body size in bytes. 0 to disable

| Header | Value |
| ------------------- | -------------------------------------------------------- |
| `Accept-Encoding` | `gzip,deflate` _(when `options.compress === true`)_ |
| `Accept` | `*/*` |
| `Connection` | `close` _(when no `options.agent` is present)_ |
| `Content-Length` | _(automatically calculated, if possible)_ |
| `Transfer-Encoding` | `chunked` _(when `req.body` is a stream)_ |
| `User-Agent` | `node-fetch (+https://github.com/node-fetch/node-fetch)` |
| Header | Value |
| ------------------- | ------------------------------------------------------ |
| `Accept-Encoding` | `gzip,deflate,br` _(when `options.compress === true`)_ |
| `Accept` | `*/*` |
| `Connection` | `close` _(when no `options.agent` is present)_ |
| `Content-Length` | _(automatically calculated, if possible)_ |
| `Transfer-Encoding` | `chunked` _(when `req.body` is a stream)_ |
| `User-Agent` | `node-fetch` |
Note: when `body` is a `Stream`, `Content-Length` is not set automatically.

@@ -518,3 +540,3 @@

Stream on Node.js have a smaller internal buffer size (16Kb, aka `highWaterMark`) from client-side browsers (>1Mb, not consistent across browsers). Because of that, when you are writing an isomorphic app and using `res.clone()`, it will hang with large response in Node.
Stream on Node.js have a smaller internal buffer size (16kB, aka `highWaterMark`) from client-side browsers (>1MB, not consistent across browsers). Because of that, when you are writing an isomorphic app and using `res.clone()`, it will hang with large response in Node.

@@ -526,5 +548,6 @@ The recommended way to fix this problem is to resolve cloned response in parallel:

fetch('https://example.com').then(res => {
const r1 = res.clone();
(async () => {
const response = await fetch('https://example.com');
const r1 = await response.clone();
return Promise.all([res.json(), r1.text()]).then(results => {

@@ -534,3 +557,3 @@ console.log(results[0]);

});
});
})();
```

@@ -543,3 +566,10 @@

fetch('https://example.com', {highWaterMark: 10}).then(res => res.clone().buffer());
(async () => {
const response = await fetch('https://example.com', {
// About 1MB
highWaterMark: 1024 * 1024
});
return res.clone().buffer();
})();
```

@@ -724,3 +754,3 @@

Since `3.x` types are bundled with `node-fetch`, so you don't need to install any additional packages.
**Since `3.x` types are bundled with `node-fetch`, so you don't need to install any additional packages.**

@@ -739,5 +769,5 @@ For older versions please use the type definitions from [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped):

[![David Frank](https://github.com/bitinn.png?size=100)](https://github.com/bitinn) | [![Jimmy Wärting](https://github.com/jimmywarting.png?size=100)](https://github.com/jimmywarting) | [![Antoni Kepinski](https://github.com/xxczaki.png?size=100)](https://github.com/xxczaki) | [![Richie Bendall](https://github.com/Richienb.png?size=100)](https://github.com/Richienb) | [![Gregor Martynus](https://github.com/gr2m.png?size=100)](https://github.com/gr2m)
---|---|---|---|---
[David Frank](https://bitinn.net/) | [Jimmy Wärting](https://jimmy.warting.se/) | [Antoni Kepinski](https://kepinski.me) | [Richie Bendall](https://www.richie-bendall.ml/) | [Gregor Martynus](https://twitter.com/gr2m)
| [![David Frank](https://github.com/bitinn.png?size=100)](https://github.com/bitinn) | [![Jimmy Wärting](https://github.com/jimmywarting.png?size=100)](https://github.com/jimmywarting) | [![Antoni Kepinski](https://github.com/xxczaki.png?size=100)](https://github.com/xxczaki) | [![Richie Bendall](https://github.com/Richienb.png?size=100)](https://github.com/Richienb) | [![Gregor Martynus](https://github.com/gr2m.png?size=100)](https://github.com/gr2m) |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| [David Frank](https://bitinn.net/) | [Jimmy Wärting](https://jimmy.warting.se/) | [Antoni Kepinski](https://kepinski.me) | [Richie Bendall](https://www.richie-bendall.ml/) | [Gregor Martynus](https://twitter.com/gr2m) |

@@ -751,3 +781,3 @@ ###### Former

MIT
[MIT](LICENSE.md)

@@ -754,0 +784,0 @@ [whatwg-fetch]: https://fetch.spec.whatwg.org/

@@ -8,7 +8,8 @@

import Stream, {PassThrough} from 'stream';
import Stream, {finished, PassThrough} from 'stream';
import {types} from 'util';
import Blob from 'fetch-blob';
import FetchError from './errors/fetch-error';
import {isBlob, isURLSearchParams, isArrayBuffer, isAbortError} from './utils/is';
import FetchError from './errors/fetch-error.js';
import {isBlob, isURLSearchParameters, isAbortError} from './utils/is.js';

@@ -26,56 +27,54 @@ const INTERNALS = Symbol('Body internals');

*/
export default function Body(body, {
size = 0,
timeout = 0
} = {}) {
if (body === null) {
// Body is undefined or null
body = null;
} else if (isURLSearchParams(body)) {
export default class Body {
constructor(body, {
size = 0
} = {}) {
if (body === null) {
// Body is undefined or null
body = null;
} else if (isURLSearchParameters(body)) {
// Body is a URLSearchParams
body = Buffer.from(body.toString());
} else if (isBlob(body)) {
// Body is blob
} else if (Buffer.isBuffer(body)) {
// Body is Buffer
} else if (isArrayBuffer(body)) {
// Body is ArrayBuffer
body = Buffer.from(body);
} else if (ArrayBuffer.isView(body)) {
// Body is ArrayBufferView
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
} else if (body instanceof Stream) {
// Body is stream
} else {
// None of the above
// coerce to string then buffer
body = Buffer.from(String(body));
}
body = Buffer.from(body.toString());
} else if (isBlob(body)) {
// Body is blob
} else if (Buffer.isBuffer(body)) {
// Body is Buffer
} else if (types.isAnyArrayBuffer(body)) {
// Body is ArrayBuffer
body = Buffer.from(body);
} else if (ArrayBuffer.isView(body)) {
// Body is ArrayBufferView
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
} else if (body instanceof Stream) {
// Body is stream
} else {
// None of the above
// coerce to string then buffer
body = Buffer.from(String(body));
}
this[INTERNALS] = {
body,
disturbed: false,
error: null
};
this.size = size;
this.timeout = timeout;
this[INTERNALS] = {
body,
disturbed: false,
error: null
};
this.size = size;
if (body instanceof Stream) {
body.on('error', err => {
const error = isAbortError(err) ?
err :
new FetchError(`Invalid response body while trying to fetch ${this.url}: ${err.message}`, 'system', err);
this[INTERNALS].error = error;
});
if (body instanceof Stream) {
body.on('error', err => {
const error = isAbortError(err) ?
err :
new FetchError(`Invalid response body while trying to fetch ${this.url}: ${err.message}`, 'system', err);
this[INTERNALS].error = error;
});
}
}
}
Body.prototype = {
get body() {
return this[INTERNALS].body;
},
}
get bodyUsed() {
return this[INTERNALS].disturbed;
},
}

@@ -87,5 +86,6 @@ /**

*/
arrayBuffer() {
return consumeBody.call(this).then(({buffer, byteOffset, byteLength}) => buffer.slice(byteOffset, byteOffset + byteLength));
},
async arrayBuffer() {
const {buffer, byteOffset, byteLength} = await consumeBody(this);
return buffer.slice(byteOffset, byteOffset + byteLength);
}

@@ -97,9 +97,11 @@ /**

*/
blob() {
const ct = this.headers && this.headers.get('content-type') || this[INTERNALS].body && this[INTERNALS].body.type || '';
return consumeBody.call(this).then(buf => new Blob([], {
async blob() {
const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS].body && this[INTERNALS].body.type) || '';
const buf = await consumeBody(this);
return new Blob([], {
type: ct.toLowerCase(),
buffer: buf
}));
},
});
}

@@ -111,5 +113,6 @@ /**

*/
json() {
return consumeBody.call(this).then(buffer => JSON.parse(buffer.toString()));
},
async json() {
const buffer = await consumeBody(this);
return JSON.parse(buffer.toString());
}

@@ -121,5 +124,6 @@ /**

*/
text() {
return consumeBody.call(this).then(buffer => buffer.toString());
},
async text() {
const buffer = await consumeBody(this);
return buffer.toString();
}

@@ -132,5 +136,5 @@ /**

buffer() {
return consumeBody.call(this);
return consumeBody(this);
}
};
}

@@ -147,12 +151,2 @@ // In browsers, all properties are enumerable.

Body.mixIn = proto => {
for (const name of Object.getOwnPropertyNames(Body.prototype)) {
// istanbul ignore else: future proof
if (!Object.prototype.hasOwnProperty.call(proto, name)) {
const desc = Object.getOwnPropertyDescriptor(Body.prototype, name);
Object.defineProperty(proto, name, desc);
}
}
};
/**

@@ -165,14 +159,14 @@ * Consume and convert an entire Body to a Buffer.

*/
function consumeBody() {
if (this[INTERNALS].disturbed) {
return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`));
const consumeBody = data => {
if (data[INTERNALS].disturbed) {
return Body.Promise.reject(new TypeError(`body used already for: ${data.url}`));
}
this[INTERNALS].disturbed = true;
data[INTERNALS].disturbed = true;
if (this[INTERNALS].error) {
return Body.Promise.reject(this[INTERNALS].error);
if (data[INTERNALS].error) {
return Body.Promise.reject(data[INTERNALS].error);
}
let {body} = this;
let {body} = data;

@@ -194,3 +188,3 @@ // Body is null

// istanbul ignore if: should never happen
/* c8 ignore next 3 */
if (!(body instanceof Stream)) {

@@ -207,24 +201,2 @@ return Body.Promise.resolve(Buffer.alloc(0));

return new Body.Promise((resolve, reject) => {
let resTimeout;
// Allow timeout on slow response body
if (this.timeout) {
resTimeout = setTimeout(() => {
abort = true;
reject(new FetchError(`Response timeout while trying to fetch ${this.url} (over ${this.timeout}ms)`, 'body-timeout'));
}, this.timeout);
}
// Handle stream errors
body.on('error', err => {
if (isAbortError(err)) {
// If the request was aborted, reject with this Error
abort = true;
reject(err);
} else {
// Other errors, such as incorrect content-encoding
reject(new FetchError(`Invalid response body while trying to fetch ${this.url}: ${err.message}`, 'system', err));
}
});
body.on('data', chunk => {

@@ -235,5 +207,5 @@ if (abort || chunk === null) {

if (this.size && accumBytes + chunk.length > this.size) {
if (data.size && accumBytes + chunk.length > data.size) {
abort = true;
reject(new FetchError(`content size at ${this.url} over limit: ${this.size}`, 'max-size'));
reject(new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size'));
return;

@@ -246,18 +218,27 @@ }

body.on('end', () => {
if (abort) {
return;
}
finished(body, {writable: false}, err => {
if (err) {
if (isAbortError(err)) {
// If the request was aborted, reject with this Error
abort = true;
reject(err);
} else {
// Other errors, such as incorrect content-encoding
reject(new FetchError(`Invalid response body while trying to fetch ${data.url}: ${err.message}`, 'system', err));
}
} else {
if (abort) {
return;
}
clearTimeout(resTimeout);
try {
resolve(Buffer.concat(accum, accumBytes));
} catch (error) {
// Handle streams that have accumulated too much data (issue #414)
reject(new FetchError(`Could not create Buffer from response body for ${this.url}: ${error.message}`, 'system', error));
try {
resolve(Buffer.concat(accum, accumBytes));
} catch (error) {
// Handle streams that have accumulated too much data (issue #414)
reject(new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error));
}
}
});
});
}
};

@@ -271,3 +252,3 @@ /**

*/
export function clone(instance, highWaterMark) {
export const clone = (instance, highWaterMark) => {
let p1;

@@ -296,3 +277,3 @@ let p2;

return body;
}
};

@@ -309,3 +290,3 @@ /**

*/
export function extractContentType(body) {
export const extractContentType = body => {
// Body is null or undefined

@@ -322,3 +303,3 @@ if (body === null) {

// Body is a URLSearchParams
if (isURLSearchParams(body)) {
if (isURLSearchParameters(body)) {
return 'application/x-www-form-urlencoded;charset=UTF-8';

@@ -333,3 +314,3 @@ }

// Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView)
if (Buffer.isBuffer(body) || isArrayBuffer(body) || ArrayBuffer.isView(body)) {
if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) {
return null;

@@ -350,3 +331,3 @@ }

return 'text/plain;charset=UTF-8';
}
};

@@ -362,3 +343,3 @@ /**

*/
export function getTotalBytes({body}) {
export const getTotalBytes = ({body}) => {
// Body is null or undefined

@@ -386,3 +367,3 @@ if (body === null) {

return null;
}
};

@@ -396,3 +377,3 @@ /**

*/
export function writeToStream(dest, {body}) {
export const writeToStream = (dest, {body}) => {
if (body === null) {

@@ -412,5 +393,5 @@ // Body is null

}
}
};
// Expose Promise
Body.Promise = global.Promise;

@@ -1,2 +0,1 @@

/**

@@ -8,2 +7,4 @@ * Headers.js

import {types} from 'util';
const invalidTokenRegex = /[^`\-\w!#$%&'*+.|~]/;

@@ -13,5 +14,5 @@ const invalidHeaderCharRegex = /[^\t\u0020-\u007E\u0080-\u00FF]/;

function validateName(name) {
name = `${name}`;
name = String(name);
if (invalidTokenRegex.test(name) || name === '') {
throw new TypeError(`${name} is not a legal HTTP header name`);
throw new TypeError(`'${name}' is not a legal HTTP header name`);
}

@@ -21,5 +22,5 @@ }

function validateValue(value) {
value = `${value}`;
value = String(value);
if (invalidHeaderCharRegex.test(value)) {
throw new TypeError(`${value} is not a legal HTTP header value`);
throw new TypeError(`'${value}' is not a legal HTTP header value`);
}

@@ -29,53 +30,38 @@ }

/**
* Find the key in the map object given a header name.
* @typedef {Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<string>[]} HeadersInit
*/
/**
* This Fetch API interface allows you to perform various actions on HTTP request and response headers.
* These actions include retrieving, setting, adding to, and removing.
* A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.
* You can add to this using methods like append() (see Examples.)
* In all methods of this interface, header names are matched by case-insensitive byte sequence.
*
* Returns undefined if not found.
*
* @param String name Header name
* @return String|Undefined
*/
function find(map, name) {
name = name.toLowerCase();
for (const key in map) {
if (key.toLowerCase() === name) {
return key;
}
}
return undefined;
}
const MAP = Symbol('map');
export default class Headers {
export default class Headers extends URLSearchParams {
/**
* Headers class
*
* @param Object headers Response headers
* @return Void
* @constructor
* @param {HeadersInit} [init] - Response headers
*/
constructor(init = undefined) {
this[MAP] = Object.create(null);
constructor(init) {
// Validate and normalize init object in [name, value(s)][]
/** @type {string[][]} */
let result = [];
if (init instanceof Headers) {
const rawHeaders = init.raw();
const headerNames = Object.keys(rawHeaders);
for (const headerName of headerNames) {
for (const value of rawHeaders[headerName]) {
this.append(headerName, value);
}
const raw = init.raw();
for (const [name, values] of Object.entries(raw)) {
result.push(...values.map(value => [name, value]));
}
return;
}
// We don't worry about converting prop to ByteString here as append()
// will handle it.
// eslint-disable-next-line no-eq-null, eqeqeq
if (init == null) {
} else if (init == null) { // eslint-disable-line no-eq-null, eqeqeq
// No op
} else if (typeof init === 'object') {
} else if (typeof init === 'object' && !types.isBoxedPrimitive(init)) {
const method = init[Symbol.iterator];
// eslint-disable-next-line no-eq-null, eqeqeq
if (method != null) {
if (method == null) {
// Record<ByteString, ByteString>
result.push(...Object.entries(init));
} else {
if (typeof method !== 'function') {

@@ -87,46 +73,93 @@ throw new TypeError('Header pairs must be iterable');

// Note: per spec we have to first exhaust the lists then process them
const pairs = [];
for (const pair of init) {
if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') {
throw new TypeError('Each header pair must be iterable');
}
result = [...init]
.map(pair => {
if (
typeof pair !== 'object' || types.isBoxedPrimitive(pair)
) {
throw new TypeError('Each header pair must be an iterable object');
}
pairs.push([...pair]);
}
return [...pair];
}).map(pair => {
if (pair.length !== 2) {
throw new TypeError('Each header pair must be a name/value tuple');
}
for (const pair of pairs) {
if (pair.length !== 2) {
throw new TypeError('Each header pair must be a name/value tuple');
}
return [...pair];
});
}
} else {
throw new TypeError('Failed to construct \'Headers\': The provided value is not of type \'(sequence<sequence<ByteString>> or record<ByteString, ByteString>)');
}
this.append(pair[0], pair[1]);
// Validate and lowercase
result =
result.length > 0 ?
result.map(([name, value]) => {
validateName(name);
validateValue(value);
return [String(name).toLowerCase(), value];
}) :
undefined;
super(result);
// Returning a Proxy that will lowercase key names, validate parameters and sort keys
// eslint-disable-next-line no-constructor-return
return new Proxy(this, {
get(target, p, receiver) {
switch (p) {
case 'append':
case 'set':
return (name, value) => {
validateName(name);
validateValue(value);
return URLSearchParams.prototype[p].call(
receiver,
String(name).toLowerCase(),
value
);
};
case 'delete':
case 'has':
case 'getAll':
return name => {
validateName(name);
return URLSearchParams.prototype[p].call(
receiver,
String(name).toLowerCase()
);
};
case 'keys':
return () => {
target.sort();
return new Set(URLSearchParams.prototype.keys.call(target)).keys();
};
default:
return Reflect.get(target, p, receiver);
}
} else {
// Record<ByteString, ByteString>
for (const key of Object.keys(init)) {
const value = init[key];
this.append(key, value);
}
}
} else {
throw new TypeError('Provided initializer must be an object');
}
/* c8 ignore next */
});
}
/**
* Return combined header value given name
*
* @param String name Header name
* @return Mixed
*/
get [Symbol.toStringTag]() {
return 'Headers';
}
toString() {
return Object.prototype.toString.call(this);
}
get(name) {
name = `${name}`;
validateName(name);
const key = find(this[MAP], name);
if (key === undefined) {
const values = this.getAll(name);
if (values.length === 0) {
return null;
}
let value = this[MAP][key].join(', ');
if (name.toLowerCase() === 'content-encoding') {
let value = values.join(', ');
if (/^content-encoding$/i.test(name)) {
value = value.toLowerCase();

@@ -138,256 +171,89 @@ }

/**
* Iterate over all headers
*
* @param Function callback Executed for each item with parameters (value, name, thisArg)
* @param Boolean thisArg `this` context for callback function
* @return Void
*/
forEach(callback, thisArg = undefined) {
let pairs = getHeaders(this);
let i = 0;
while (i < pairs.length) {
const [name, value] = pairs[i];
callback.call(thisArg, value, name, this);
pairs = getHeaders(this);
i++;
forEach(callback) {
for (const name of this.keys()) {
callback(this.get(name), name);
}
}
/**
* Overwrite header values given name
*
* @param String name Header name
* @param String value Header value
* @return Void
*/
set(name, value) {
name = `${name}`;
value = `${value}`;
validateName(name);
validateValue(value);
const key = find(this[MAP], name);
this[MAP][key !== undefined ? key : name] = [value];
* values() {
for (const name of this.keys()) {
yield this.get(name);
}
}
/**
* Append a value onto existing header
*
* @param String name Header name
* @param String value Header value
* @return Void
* @type {() => IterableIterator<[string, string]>}
*/
append(name, value) {
name = `${name}`;
value = `${value}`;
validateName(name);
validateValue(value);
const key = find(this[MAP], name);
if (key !== undefined) {
this[MAP][key].push(value);
} else {
this[MAP][name] = [value];
* entries() {
for (const name of this.keys()) {
yield [name, this.get(name)];
}
}
/**
* Check for header name existence
*
* @param String name Header name
* @return Boolean
*/
has(name) {
name = `${name}`;
validateName(name);
return find(this[MAP], name) !== undefined;
[Symbol.iterator]() {
return this.entries();
}
/**
* Delete all header values given name
*
* @param String name Header name
* @return Void
* Node-fetch non-spec method
* returning all headers and their values as array
* @returns {Record<string, string[]>}
*/
delete(name) {
name = `${name}`;
validateName(name);
const key = find(this[MAP], name);
if (key !== undefined) {
delete this[MAP][key];
}
}
/**
* Return raw headers (non-spec api)
*
* @return Object
*/
raw() {
return this[MAP];
return [...this.keys()].reduce((result, key) => {
result[key] = this.getAll(key);
return result;
}, {});
}
/**
* Get an iterator on keys.
*
* @return Iterator
* For better console.log(headers) and also to convert Headers into Node.js Request compatible format
*/
keys() {
return createHeadersIterator(this, 'key');
}
[Symbol.for('nodejs.util.inspect.custom')]() {
return [...this.keys()].reduce((result, key) => {
const values = this.getAll(key);
// Http.request() only supports string as Host header.
// This hack makes specifying custom Host header possible.
if (key === 'host') {
result[key] = values[0];
} else {
result[key] = values.length > 1 ? values : values[0];
}
/**
* Get an iterator on values.
*
* @return Iterator
*/
values() {
return createHeadersIterator(this, 'value');
return result;
}, {});
}
/**
* Get an iterator on entries.
*
* This is the default iterator of the Headers object.
*
* @return Iterator
*/
[Symbol.iterator]() {
return createHeadersIterator(this, 'key+value');
}
}
Headers.prototype.entries = Headers.prototype[Symbol.iterator];
Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
value: 'Headers',
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperties(Headers.prototype, {
get: {enumerable: true},
forEach: {enumerable: true},
set: {enumerable: true},
append: {enumerable: true},
has: {enumerable: true},
delete: {enumerable: true},
keys: {enumerable: true},
values: {enumerable: true},
entries: {enumerable: true}
});
function getHeaders(headers, kind = 'key+value') {
const keys = Object.keys(headers[MAP]).sort();
return keys.map(
kind === 'key' ?
k => k.toLowerCase() :
(kind === 'value' ?
k => headers[MAP][k].join(', ') :
k => [k.toLowerCase(), headers[MAP][k].join(', ')])
);
}
const INTERNAL = Symbol('internal');
function createHeadersIterator(target, kind) {
const iterator = Object.create(HeadersIteratorPrototype);
iterator[INTERNAL] = {
target,
kind,
index: 0
};
return iterator;
}
const HeadersIteratorPrototype = Object.setPrototypeOf({
next() {
// istanbul ignore if
if (!this ||
Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
throw new TypeError('Value of `this` is not a HeadersIterator');
}
const {
target,
kind,
index
} = this[INTERNAL];
const values = getHeaders(target, kind);
const length_ = values.length;
if (index >= length_) {
return {
value: undefined,
done: true
};
}
this[INTERNAL].index = index + 1;
return {
value: values[index],
done: false
};
}
}, Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
));
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, {
value: 'HeadersIterator',
writable: false,
enumerable: false,
configurable: true
});
/**
* Export the Headers object in a form that Node.js can consume.
*
* @param Headers headers
* @return Object
* Re-shaping object for Web IDL tests
* Only need to do it for overridden methods
*/
export function exportNodeCompatibleHeaders(headers) {
const object = {__proto__: null, ...headers[MAP]};
Object.defineProperties(
Headers.prototype,
['get', 'entries', 'forEach', 'values'].reduce((result, property) => {
result[property] = {enumerable: true};
return result;
}, {})
);
// Http.request() only supports string as Host header. This hack makes
// specifying custom Host header possible.
const hostHeaderKey = find(headers[MAP], 'Host');
if (hostHeaderKey !== undefined) {
object[hostHeaderKey] = object[hostHeaderKey][0];
}
return object;
}
/**
* Create a Headers object from an object of headers, ignoring those that do
* Create a Headers object from an http.IncomingMessage.rawHeaders, ignoring those that do
* not conform to HTTP grammar productions.
*
* @param Object obj Object of headers
* @return Headers
* @param {import('http').IncomingMessage['rawHeaders']} headers
*/
export function createHeadersLenient(object) {
const headers = new Headers();
for (const name of Object.keys(object)) {
if (invalidTokenRegex.test(name)) {
continue;
}
if (Array.isArray(object[name])) {
for (const value of object[name]) {
if (invalidHeaderCharRegex.test(value)) {
continue;
export function fromRawHeaders(headers = []) {
return new Headers(
headers
// Split into pairs
.reduce((result, value, index, array) => {
if (index % 2 === 0) {
result.push(array.slice(index, index + 2));
}
if (headers[MAP][name] === undefined) {
headers[MAP][name] = [value];
} else {
headers[MAP][name].push(value);
}
}
} else if (!invalidHeaderCharRegex.test(object[name])) {
headers[MAP][name] = [object[name]];
}
}
return result;
}, [])
.filter(([name, value]) => !(invalidTokenRegex.test(name) || invalidHeaderCharRegex.test(value)))
return headers;
);
}

@@ -15,9 +15,12 @@ /**

import Body, {writeToStream, getTotalBytes} from './body';
import Response from './response';
import Headers, {createHeadersLenient} from './headers';
import Request, {getNodeRequestOptions} from './request';
import FetchError from './errors/fetch-error';
import AbortError from './errors/abort-error';
import Body, {writeToStream, getTotalBytes} from './body.js';
import Response from './response.js';
import Headers, {fromRawHeaders} from './headers.js';
import Request, {getNodeRequestOptions} from './request.js';
import FetchError from './errors/fetch-error.js';
import AbortError from './errors/abort-error.js';
import {isRedirect} from './utils/is-redirect.js';
export {Headers, Request, Response, FetchError, AbortError, isRedirect};
/**

@@ -30,3 +33,3 @@ * Fetch function

*/
export default function fetch(url, options_) {
const fetch = (url, options_) => {
// Allow custom promise

@@ -43,4 +46,4 @@ if (!fetch.Promise) {

const data = dataURIToBuffer(url);
const res = new Response(data, {headers: {'Content-Type': data.type}});
return fetch.Promise.resolve(res);
const response = new Response(data, {headers: {'Content-Type': data.type}});
return fetch.Promise.resolve(response);
}

@@ -97,3 +100,3 @@

function finalize() {
const finalize = () => {
request_.abort();

@@ -103,11 +106,4 @@ if (signal) {

}
}
};
if (request.timeout) {
request_.setTimeout(request.timeout, () => {
finalize();
reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout'));
});
}
request_.on('error', err => {

@@ -118,7 +114,8 @@ reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));

request_.on('response', res => {
const headers = createHeadersLenient(res.headers);
request_.on('response', response_ => {
request_.setTimeout(0);
const headers = fromRawHeaders(response_.rawHeaders);
// HTTP fetch step 5
if (fetch.isRedirect(res.statusCode)) {
if (isRedirect(response_.statusCode)) {
// HTTP fetch step 5.2

@@ -142,4 +139,4 @@ const location = headers.get('Location');

headers.set('Location', locationURL);
/* c8 ignore next 3 */
} catch (error) {
// istanbul ignore next: nodejs server prevent invalid response headers, we can't test this through normal request
reject(error);

@@ -173,8 +170,7 @@ }

body: request.body,
signal: request.signal,
timeout: request.timeout
signal: request.signal
};
// HTTP-redirect fetch step 9
if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) {
if (response_.statusCode !== 303 && request.body && getTotalBytes(request) === null) {
reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));

@@ -186,3 +182,3 @@ finalize();

// HTTP-redirect fetch step 11
if (res.statusCode === 303 || ((res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST')) {
if (response_.statusCode === 303 || ((response_.statusCode === 301 || response_.statusCode === 302) && request.method === 'POST')) {
requestOptions.method = 'GET';

@@ -205,3 +201,3 @@ requestOptions.body = undefined;

// Prepare response
res.once('end', () => {
response_.once('end', () => {
if (signal) {

@@ -212,3 +208,3 @@ signal.removeEventListener('abort', abortAndFinalize);

let body = pump(res, new PassThrough(), error => {
let body = pump(response_, new PassThrough(), error => {
reject(error);

@@ -219,7 +215,6 @@ });

url: request.url,
status: res.statusCode,
statusText: res.statusMessage,
status: response_.statusCode,
statusText: response_.statusMessage,
headers,
size: request.size,
timeout: request.timeout,
counter: request.counter,

@@ -240,3 +235,3 @@ highWaterMark: request.highWaterMark

// 5. content not modified response (304)
if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) {
if (!request.compress || request.method === 'HEAD' || codings === null || response_.statusCode === 204 || response_.statusCode === 304) {
response = new Response(body, responseOptions);

@@ -271,3 +266,3 @@ resolve(response);

// a hack for old IIS and Apache servers
const raw = pump(res, new PassThrough(), error => {
const raw = pump(response_, new PassThrough(), error => {
reject(error);

@@ -294,3 +289,3 @@ });

// For br
if (codings === 'br' && typeof zlib.createBrotliDecompress === 'function') {
if (codings === 'br') {
body = pump(body, zlib.createBrotliDecompress(), error => {

@@ -311,17 +306,7 @@ reject(error);

});
}
};
/**
* Redirect code matching
*
* @param Number code Status code
* @return Boolean
*/
fetch.isRedirect = code => [301, 302, 303, 307, 308].includes(code);
export default fetch;
// Expose Promise
fetch.Promise = global.Promise;
fetch.Headers = Headers;
fetch.Request = Request;
fetch.Response = Response;
fetch.FetchError = FetchError;

@@ -11,12 +11,9 @@

import {format as formatUrl} from 'url';
import Stream from 'stream';
import Headers, {exportNodeCompatibleHeaders} from './headers';
import Body, {clone, extractContentType, getTotalBytes} from './body';
import {isAbortSignal} from './utils/is';
import {getSearch} from './utils/get-search';
import Headers from './headers.js';
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
import {isAbortSignal} from './utils/is.js';
import {getSearch} from './utils/get-search.js';
const INTERNALS = Symbol('Request internals');
const streamDestructionSupported = 'destroy' in Stream.Readable.prototype;
/**

@@ -28,3 +25,3 @@ * Check if `obj` is an instance of Request.

*/
function isRequest(object) {
const isRequest = object => {
return (

@@ -34,3 +31,3 @@ typeof object === 'object' &&

);
}
};

@@ -43,3 +40,3 @@ /**

*/
function parseURL(urlString) {
const parseURL = urlString => {
/*

@@ -56,3 +53,3 @@ Check whether the URL is absolute or not

throw new TypeError('Only absolute URLs are supported');
}
};

@@ -66,3 +63,3 @@ /**

*/
export default class Request {
export default class Request extends Body {
constructor(input, init = {}) {

@@ -72,3 +69,5 @@ let parsedURL;

// Normalize input and force URL to be encoded as UTF-8 (https://github.com/bitinn/node-fetch/issues/245)
if (!isRequest(input)) {
if (isRequest(input)) {
parsedURL = parseURL(input.url);
} else {
if (input && input.href) {

@@ -85,4 +84,2 @@ // In order to support Node.js' Url objects; though WHATWG's URL objects

input = {};
} else {
parsedURL = parseURL(input.url);
}

@@ -94,3 +91,3 @@

// eslint-disable-next-line no-eq-null, eqeqeq
if ((init.body != null || isRequest(input) && input.body !== null) &&
if (((init.body != null || isRequest(input)) && input.body !== null) &&
(method === 'GET' || method === 'HEAD')) {

@@ -100,4 +97,3 @@ throw new TypeError('Request with GET/HEAD method cannot have body');

// eslint-disable-next-line no-eq-null, eqeqeq
const inputBody = init.body != null ?
const inputBody = init.body ?
init.body :

@@ -108,4 +104,3 @@ (isRequest(input) && input.body !== null ?

Body.call(this, inputBody, {
timeout: init.timeout || input.timeout || 0,
super(inputBody, {
size: init.size || input.size || 0

@@ -143,11 +138,7 @@ });

// Node-fetch-only options
this.follow = init.follow !== undefined ?
init.follow : (input.follow !== undefined ?
input.follow : 20);
this.compress = init.compress !== undefined ?
init.compress : (input.compress !== undefined ?
input.compress : true);
this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow;
this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress;
this.counter = init.counter || input.counter || 0;
this.agent = init.agent || input.agent;
this.highWaterMark = init.highWaterMark || input.highWaterMark;
this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
}

@@ -183,13 +174,8 @@

}
get [Symbol.toStringTag]() {
return 'Request';
}
}
Body.mixIn(Request.prototype);
Object.defineProperty(Request.prototype, Symbol.toStringTag, {
value: 'Request',
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperties(Request.prototype, {

@@ -210,3 +196,3 @@ method: {enumerable: true},

*/
export function getNodeRequestOptions(request) {
export const getNodeRequestOptions = request => {
const {parsedURL} = request[INTERNALS];

@@ -220,7 +206,2 @@ const headers = new Headers(request[INTERNALS].headers);

// Basic fetch
if (!parsedURL.protocol || !parsedURL.hostname) {
throw new TypeError('Only absolute URLs are supported');
}
if (!/^https?:$/.test(parsedURL.protocol)) {

@@ -230,10 +211,2 @@ throw new TypeError('Only HTTP(S) protocols are supported');

if (
request.signal &&
request.body instanceof Stream.Readable &&
!streamDestructionSupported
) {
throw new Error('Cancellation of streamed requests with AbortSignal is not supported');
}
// HTTP-network-or-cache fetch steps 2.4-2.7

@@ -258,3 +231,3 @@ let contentLengthValue = null;

if (!headers.has('User-Agent')) {
headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)');
headers.set('User-Agent', 'node-fetch');
}

@@ -264,3 +237,3 @@

if (request.compress && !headers.has('Accept-Encoding')) {
headers.set('Accept-Encoding', 'gzip,deflate');
headers.set('Accept-Encoding', 'gzip,deflate,br');
}

@@ -294,3 +267,3 @@

method: request.method,
headers: exportNodeCompatibleHeaders(headers),
headers: headers[Symbol.for('nodejs.util.inspect.custom')](),
agent

@@ -300,2 +273,2 @@ };

return requestOptions;
}
};

@@ -7,4 +7,5 @@ /**

import Headers from './headers';
import Body, {clone, extractContentType} from './body';
import Headers from './headers.js';
import Body, {clone, extractContentType} from './body.js';
import {isRedirect} from './utils/is-redirect.js';

@@ -20,5 +21,5 @@ const INTERNALS = Symbol('Response internals');

*/
export default class Response {
export default class Response extends Body {
constructor(body = null, options = {}) {
Body.call(this, body, options);
super(body, options);

@@ -89,4 +90,3 @@ const status = options.status || 200;

redirected: this.redirected,
size: this.size,
timeout: this.timeout
size: this.size
});

@@ -101,3 +101,3 @@ }

static redirect(url, status = 302) {
if (![301, 302, 303, 307, 308].includes(status)) {
if (!isRedirect(status)) {
throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');

@@ -113,6 +113,8 @@ }

}
get [Symbol.toStringTag]() {
return 'Response';
}
}
Body.mixIn(Response.prototype);
Object.defineProperties(Response.prototype, {

@@ -128,7 +130,1 @@ url: {enumerable: true},

Object.defineProperty(Response.prototype, Symbol.toStringTag, {
value: 'Response',
writable: false,
enumerable: false,
configurable: true
});

@@ -1,2 +0,2 @@

export function getSearch(parsedURL) {
export const getSearch = parsedURL => {
if (parsedURL.search) {

@@ -9,2 +9,2 @@ return parsedURL.search;

return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : '';
}
};

@@ -16,3 +16,3 @@ /**

*/
export function isURLSearchParams(object) {
export const isURLSearchParameters = object => {
return (

@@ -29,3 +29,3 @@ typeof object === 'object' &&

);
}
};

@@ -38,3 +38,3 @@ /**

*/
export function isBlob(object) {
export const isBlob = object => {
return (

@@ -48,3 +48,3 @@ typeof object === 'object' &&

);
}
};

@@ -57,3 +57,3 @@ /**

*/
export function isAbortSignal(object) {
export const isAbortSignal = object => {
return (

@@ -63,15 +63,5 @@ typeof object === 'object' &&

);
}
};
/**
* Check if `obj` is an instance of ArrayBuffer.
*
* @param {*} obj
* @return {boolean}
*/
export function isArrayBuffer(object) {
return object[NAME] === 'ArrayBuffer';
}
/**
* Check if `obj` is an instance of AbortError.

@@ -82,4 +72,4 @@ *

*/
export function isAbortError(object) {
export const isAbortError = object => {
return object[NAME] === 'AbortError';
}
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc