make-fetch-happen
Advanced tools
Comparing version 2.2.2 to 2.2.3
@@ -5,2 +5,13 @@ # Change Log | ||
<a name="2.2.3"></a> | ||
## [2.2.3](https://github.com/zkat/make-fetch-happen/compare/v2.2.2...v2.2.3) (2017-04-18) | ||
### Bug Fixes | ||
* **staleness:** responses older than 8h were never stale :< ([b54dd75](https://github.com/zkat/make-fetch-happen/commit/b54dd75)) | ||
* **warning:** remove spurious warning, make format more spec-compliant ([2e4f6bb](https://github.com/zkat/make-fetch-happen/commit/2e4f6bb)) | ||
<a name="2.2.2"></a> | ||
@@ -7,0 +18,0 @@ ## [2.2.2](https://github.com/zkat/make-fetch-happen/compare/v2.2.1...v2.2.2) (2017-04-12) |
80
index.js
@@ -13,2 +13,3 @@ 'use strict' | ||
const USER_AGENT = `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})` | ||
const RETRY_ERRORS = [ | ||
@@ -101,2 +102,11 @@ 'ECONNRESET', // remote socket closed on us | ||
// https://tools.ietf.org/html/rfc7234#section-4.3.4 | ||
// | ||
// If a stored response is selected for update, the cache MUST: | ||
// | ||
// * delete any Warning header fields in the stored response with | ||
// warn-code 1xx (see Section 5.5); | ||
// | ||
// * retain any Warning header fields in the stored response with | ||
// warn-code 2xx; | ||
// | ||
res.headers.delete('Warning') | ||
@@ -112,2 +122,7 @@ } | ||
)) { | ||
// 112 Disconnected operation | ||
// SHOULD be included if the cache is intentionally disconnected from | ||
// the rest of the network for a period of time. | ||
// (https://tools.ietf.org/html/rfc2616#section-14.46) | ||
setWarning(res, 112, 'Disconnected operation') | ||
return res | ||
@@ -163,3 +178,9 @@ } else if (!res && opts.cache === 'only-if-cached') { | ||
} | ||
const bool = !makePolicy(req, res).satisfiesWithoutRevalidation(_req) | ||
const policy = makePolicy(req, res) | ||
policy._responseTime = new Date( | ||
res.headers.get('x-local-cache-time') || | ||
res.headers.get('date') || | ||
0 // better to pretend everything is stale | ||
) | ||
const bool = !policy.satisfiesWithoutRevalidation(_req) | ||
return bool | ||
@@ -189,21 +210,16 @@ } | ||
}) | ||
if (condRes.status === 304) { | ||
if (condRes.status >= 500 && !mustRevalidate(cachedRes)) { | ||
// 111 Revalidation failed | ||
// MUST be included if a cache returns a stale response because an | ||
// attempt to revalidate the response failed, due to an inability to | ||
// reach the server. | ||
// (https://tools.ietf.org/html/rfc2616#section-14.46) | ||
setWarning(cachedRes, 111, `Revalidation failed`) | ||
return cachedRes | ||
} else if (condRes.status === 304) { | ||
condRes.body = cachedRes.body | ||
return opts.cacheManager.put(req, condRes, opts).then(newRes => { | ||
newRes.headers = new fetch.Headers(revaled.policy.responseHeaders()) | ||
if (revaled.modified) { | ||
setWarning(newRes, 110, 'Revalidation failed even with 304 response. Using stale body with new headers.') | ||
} else { | ||
setWarning(newRes, 110, 'Local cached response stale') | ||
} | ||
return newRes | ||
}) | ||
} else if ( | ||
condRes.status >= 500 && | ||
!mustRevalidate(cachedRes) | ||
) { | ||
setWarning( | ||
cachedRes, 111, `Revalidation failed with status ${condRes.status}. Returning stale response` | ||
) | ||
return cachedRes | ||
} else { | ||
@@ -218,3 +234,15 @@ return condRes | ||
} else { | ||
setWarning(cachedRes, 111, `${err.code}: ${err.message}`) | ||
// 111 Revalidation failed | ||
// MUST be included if a cache returns a stale response because an | ||
// attempt to revalidate the response failed, due to an inability to | ||
// reach the server. | ||
// (https://tools.ietf.org/html/rfc2616#section-14.46) | ||
setWarning(cachedRes, 111, `Revalidation failed`) | ||
// 199 Miscellaneous warning | ||
// The warning text MAY include arbitrary information to be presented to | ||
// a human user, or logged. A system receiving this warning MUST NOT take | ||
// any automated action, besides presenting the warning to the user. | ||
// (https://tools.ietf.org/html/rfc2616#section-14.46) | ||
setWarning( | ||
cachedRes, 199, `Miscellaneous Warning ${err.code}: ${err.message}`) | ||
return cachedRes | ||
@@ -225,10 +253,18 @@ } | ||
function setWarning (reqOrRes, code, message, host, append) { | ||
host = host || 'localhost' | ||
reqOrRes.headers[append ? 'append' : 'set']( | ||
function setWarning (reqOrRes, code, message, replace) { | ||
// Warning = "Warning" ":" 1#warning-value | ||
// warning-value = warn-code SP warn-agent SP warn-text [SP warn-date] | ||
// warn-code = 3DIGIT | ||
// warn-agent = ( host [ ":" port ] ) | pseudonym | ||
// ; the name or pseudonym of the server adding | ||
// ; the Warning header, for use in debugging | ||
// warn-text = quoted-string | ||
// warn-date = <"> HTTP-date <"> | ||
// (https://tools.ietf.org/html/rfc2616#section-14.46) | ||
reqOrRes.headers[replace ? 'set' : 'append']( | ||
'Warning', | ||
`${code} ${host} ${ | ||
`${code} ${url.parse(reqOrRes.url).host} ${ | ||
JSON.stringify(message) | ||
} ${ | ||
JSON.stringify(new Date().toUTCString) | ||
JSON.stringify(new Date().toUTCString()) | ||
}` | ||
@@ -242,3 +278,3 @@ ) | ||
'connection': agent ? 'keep-alive' : 'close', | ||
'user-agent': `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})` | ||
'user-agent': USER_AGENT | ||
} | ||
@@ -245,0 +281,0 @@ if (opts.headers) { |
{ | ||
"_args": [ | ||
[ | ||
{ | ||
"raw": "encoding@^0.1.11", | ||
"scope": null, | ||
"escapedName": "encoding", | ||
"name": "encoding", | ||
"rawSpec": "^0.1.11", | ||
"spec": ">=0.1.11 <0.2.0", | ||
"type": "range" | ||
}, | ||
"/Users/zkat/Documents/code/make-fetch-happen/node-fetch-pkg.tgz" | ||
] | ||
], | ||
"_from": "encoding@>=0.1.11 <0.2.0", | ||
"_id": "encoding@0.1.12", | ||
"_inCache": true, | ||
"_integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", | ||
"_location": "/encoding", | ||
"_nodeVersion": "5.3.0", | ||
"_npmUser": { | ||
"name": "andris", | ||
"email": "andris@kreata.ee" | ||
}, | ||
"_npmVersion": "3.3.12", | ||
"_phantomChildren": {}, | ||
"_requested": { | ||
"type": "range", | ||
"registry": true, | ||
"raw": "encoding@^0.1.11", | ||
"scope": null, | ||
"name": "encoding", | ||
"escapedName": "encoding", | ||
"name": "encoding", | ||
"rawSpec": "^0.1.11", | ||
"spec": ">=0.1.11 <0.2.0", | ||
"type": "range" | ||
"saveSpec": null, | ||
"fetchSpec": "^0.1.11" | ||
}, | ||
@@ -40,15 +20,17 @@ "_requiredBy": [ | ||
"_resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", | ||
"_shasum": "538b66f3ee62cd1ab51ec323829d1f9480c74beb", | ||
"_shrinkwrap": null, | ||
"_spec": "encoding@^0.1.11", | ||
"_where": "/Users/zkat/Documents/code/make-fetch-happen/node-fetch-pkg.tgz", | ||
"_where": "/Users/zkat/Documents/code/make-fetch-happen/node_modules/node-fetch", | ||
"author": { | ||
"name": "Andris Reinman" | ||
}, | ||
"bin": null, | ||
"bugs": { | ||
"url": "https://github.com/andris9/encoding/issues" | ||
}, | ||
"bundleDependencies": false, | ||
"dependencies": { | ||
"iconv-lite": "~0.4.13" | ||
}, | ||
"deprecated": false, | ||
"description": "Convert encodings, uses iconv by default and fallbacks to iconv-lite if needed", | ||
@@ -59,19 +41,8 @@ "devDependencies": { | ||
}, | ||
"directories": {}, | ||
"dist": { | ||
"shasum": "538b66f3ee62cd1ab51ec323829d1f9480c74beb", | ||
"tarball": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz" | ||
}, | ||
"gitHead": "91ae950aaa854a119122c27cdbabd8c5585106f7", | ||
"homepage": "https://github.com/andris9/encoding#readme", | ||
"license": "MIT", | ||
"main": "lib/encoding.js", | ||
"maintainers": [ | ||
{ | ||
"name": "andris", | ||
"email": "andris@node.ee" | ||
} | ||
], | ||
"name": "encoding", | ||
"optionalDependencies": {}, | ||
"peerDependencies": {}, | ||
"readme": "# Encoding\n\n**encoding** is a simple wrapper around [node-iconv](https://github.com/bnoordhuis/node-iconv) and [iconv-lite](https://github.com/ashtuchkin/iconv-lite/) to convert strings from one encoding to another. If node-iconv is not available for some reason,\niconv-lite will be used instead of it as a fallback.\n\n[![Build Status](https://secure.travis-ci.org/andris9/encoding.svg)](http://travis-ci.org/andris9/Nodemailer)\n[![npm version](https://badge.fury.io/js/encoding.svg)](http://badge.fury.io/js/encoding)\n\n## Install\n\nInstall through npm\n\n npm install encoding\n\n## Usage\n\nRequire the module\n\n var encoding = require(\"encoding\");\n\nConvert with encoding.convert()\n\n var resultBuffer = encoding.convert(text, toCharset, fromCharset);\n\nWhere\n\n * **text** is either a Buffer or a String to be converted\n * **toCharset** is the characterset to convert the string\n * **fromCharset** (*optional*, defaults to UTF-8) is the source charset\n\nOutput of the conversion is always a Buffer object.\n\nExample\n\n var result = encoding.convert(\"ÕÄÖÜ\", \"Latin_1\");\n console.log(result); //<Buffer d5 c4 d6 dc>\n\n## iconv support\n\nBy default only iconv-lite is bundled. If you need node-iconv support, you need to add it\nas an additional dependency for your project:\n\n ...,\n \"dependencies\":{\n \"encoding\": \"*\",\n \"iconv\": \"*\"\n },\n ...\n\n## License\n\n**MIT**\n", | ||
@@ -78,0 +49,0 @@ "readmeFilename": "README.md", |
{ | ||
"_args": [ | ||
[ | ||
{ | ||
"raw": "iconv-lite@~0.4.13", | ||
"scope": null, | ||
"escapedName": "iconv-lite", | ||
"name": "iconv-lite", | ||
"rawSpec": "~0.4.13", | ||
"spec": ">=0.4.13 <0.5.0", | ||
"type": "range" | ||
}, | ||
"/Users/zkat/Documents/code/make-fetch-happen/node_modules/encoding" | ||
] | ||
], | ||
"_from": "iconv-lite@>=0.4.13 <0.5.0", | ||
"_id": "iconv-lite@0.4.15", | ||
"_inCache": true, | ||
"_integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", | ||
"_location": "/iconv-lite", | ||
"_nodeVersion": "7.0.0", | ||
"_npmOperationalInternal": { | ||
"host": "packages-18-east.internal.npmjs.com", | ||
"tmp": "tmp/iconv-lite-0.4.15.tgz_1479754977280_0.752664492232725" | ||
}, | ||
"_npmUser": { | ||
"name": "ashtuchkin", | ||
"email": "ashtuchkin@gmail.com" | ||
}, | ||
"_npmVersion": "2.15.9", | ||
"_phantomChildren": {}, | ||
"_requested": { | ||
"type": "range", | ||
"registry": true, | ||
"raw": "iconv-lite@~0.4.13", | ||
"scope": null, | ||
"name": "iconv-lite", | ||
"escapedName": "iconv-lite", | ||
"name": "iconv-lite", | ||
"rawSpec": "~0.4.13", | ||
"spec": ">=0.4.13 <0.5.0", | ||
"type": "range" | ||
"saveSpec": null, | ||
"fetchSpec": "~0.4.13" | ||
}, | ||
@@ -44,3 +20,2 @@ "_requiredBy": [ | ||
"_resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", | ||
"_shasum": "fe265a218ac6a57cfe854927e9d04c19825eddeb", | ||
"_shrinkwrap": null, | ||
@@ -53,2 +28,3 @@ "_spec": "iconv-lite@~0.4.13", | ||
}, | ||
"bin": null, | ||
"browser": { | ||
@@ -61,2 +37,3 @@ "./extend-node": false, | ||
}, | ||
"bundleDependencies": false, | ||
"contributors": [ | ||
@@ -113,2 +90,3 @@ { | ||
"dependencies": {}, | ||
"deprecated": false, | ||
"description": "Convert character encodings in pure javascript.", | ||
@@ -124,11 +102,5 @@ "devDependencies": { | ||
}, | ||
"directories": {}, | ||
"dist": { | ||
"shasum": "fe265a218ac6a57cfe854927e9d04c19825eddeb", | ||
"tarball": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz" | ||
}, | ||
"engines": { | ||
"node": ">=0.10.0" | ||
}, | ||
"gitHead": "c3bcedcd6a5025c25e39ed1782347acaed1d290f", | ||
"homepage": "https://github.com/ashtuchkin/iconv-lite", | ||
@@ -143,10 +115,5 @@ "keywords": [ | ||
"main": "./lib/index.js", | ||
"maintainers": [ | ||
{ | ||
"name": "ashtuchkin", | ||
"email": "ashtuchkin@gmail.com" | ||
} | ||
], | ||
"name": "iconv-lite", | ||
"optionalDependencies": {}, | ||
"peerDependencies": {}, | ||
"readme": "## Pure JS character encoding conversion [![Build Status](https://travis-ci.org/ashtuchkin/iconv-lite.svg?branch=master)](https://travis-ci.org/ashtuchkin/iconv-lite)\n\n * Doesn't need native code compilation. Works on Windows and in sandboxed environments like [Cloud9](http://c9.io).\n * Used in popular projects like [Express.js (body_parser)](https://github.com/expressjs/body-parser), \n [Grunt](http://gruntjs.com/), [Nodemailer](http://www.nodemailer.com/), [Yeoman](http://yeoman.io/) and others.\n * Faster than [node-iconv](https://github.com/bnoordhuis/node-iconv) (see below for performance comparison).\n * Intuitive encode/decode API\n * Streaming support for Node v0.10+\n * [Deprecated] Can extend Node.js primitives (buffers, streams) to support all iconv-lite encodings.\n * In-browser usage via [Browserify](https://github.com/substack/node-browserify) (~180k gzip compressed with Buffer shim included).\n * Typescript [type definition file](https://github.com/ashtuchkin/iconv-lite/blob/master/lib/index.d.ts) included.\n * License: MIT.\n\n[![NPM Stats](https://nodei.co/npm/iconv-lite.png?downloads=true&downloadRank=true)](https://npmjs.org/packages/iconv-lite/)\n\n## Usage\n### Basic API\n```javascript\nvar iconv = require('iconv-lite');\n\n// Convert from an encoded buffer to js string.\nstr = iconv.decode(new Buffer([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251');\n\n// Convert from js string to an encoded buffer.\nbuf = iconv.encode(\"Sample input string\", 'win1251');\n\n// Check if encoding is supported\niconv.encodingExists(\"us-ascii\")\n```\n\n### Streaming API (Node v0.10+)\n```javascript\n\n// Decode stream (from binary stream to js strings)\nhttp.createServer(function(req, res) {\n var converterStream = iconv.decodeStream('win1251');\n req.pipe(converterStream);\n\n converterStream.on('data', function(str) {\n console.log(str); // Do something with decoded strings, chunk-by-chunk.\n });\n});\n\n// Convert encoding streaming example\nfs.createReadStream('file-in-win1251.txt')\n .pipe(iconv.decodeStream('win1251'))\n .pipe(iconv.encodeStream('ucs2'))\n .pipe(fs.createWriteStream('file-in-ucs2.txt'));\n\n// Sugar: all encode/decode streams have .collect(cb) method to accumulate data.\nhttp.createServer(function(req, res) {\n req.pipe(iconv.decodeStream('win1251')).collect(function(err, body) {\n assert(typeof body == 'string');\n console.log(body); // full request body string\n });\n});\n```\n\n### [Deprecated] Extend Node.js own encodings\n> NOTE: This doesn't work on latest Node versions. See [details](https://github.com/ashtuchkin/iconv-lite/wiki/Node-v4-compatibility).\n\n```javascript\n// After this call all Node basic primitives will understand iconv-lite encodings.\niconv.extendNodeEncodings();\n\n// Examples:\nbuf = new Buffer(str, 'win1251');\nbuf.write(str, 'gbk');\nstr = buf.toString('latin1');\nassert(Buffer.isEncoding('iso-8859-15'));\nBuffer.byteLength(str, 'us-ascii');\n\nhttp.createServer(function(req, res) {\n req.setEncoding('big5');\n req.collect(function(err, body) {\n console.log(body);\n });\n});\n\nfs.createReadStream(\"file.txt\", \"shift_jis\");\n\n// External modules are also supported (if they use Node primitives, which they probably do).\nrequest = require('request');\nrequest({\n url: \"http://github.com/\", \n encoding: \"cp932\"\n});\n\n// To remove extensions\niconv.undoExtendNodeEncodings();\n```\n\n## Supported encodings\n\n * All node.js native encodings: utf8, ucs2 / utf16-le, ascii, binary, base64, hex.\n * Additional unicode encodings: utf16, utf16-be, utf-7, utf-7-imap.\n * All widespread singlebyte encodings: Windows 125x family, ISO-8859 family, \n IBM/DOS codepages, Macintosh family, KOI8 family, all others supported by iconv library. \n Aliases like 'latin1', 'us-ascii' also supported.\n * All widespread multibyte encodings: CP932, CP936, CP949, CP950, GB2313, GBK, GB18030, Big5, Shift_JIS, EUC-JP.\n\nSee [all supported encodings on wiki](https://github.com/ashtuchkin/iconv-lite/wiki/Supported-Encodings).\n\nMost singlebyte encodings are generated automatically from [node-iconv](https://github.com/bnoordhuis/node-iconv). Thank you Ben Noordhuis and libiconv authors!\n\nMultibyte encodings are generated from [Unicode.org mappings](http://www.unicode.org/Public/MAPPINGS/) and [WHATWG Encoding Standard mappings](http://encoding.spec.whatwg.org/). Thank you, respective authors!\n\n\n## Encoding/decoding speed\n\nComparison with node-iconv module (1000x256kb, on MacBook Pro, Core i5/2.6 GHz, Node v0.12.0). \nNote: your results may vary, so please always check on your hardware.\n\n operation iconv@2.1.4 iconv-lite@0.4.7\n ----------------------------------------------------------\n encode('win1251') ~96 Mb/s ~320 Mb/s\n decode('win1251') ~95 Mb/s ~246 Mb/s\n\n## BOM handling\n\n * Decoding: BOM is stripped by default, unless overridden by passing `stripBOM: false` in options\n (f.ex. `iconv.decode(buf, enc, {stripBOM: false})`).\n A callback might also be given as a `stripBOM` parameter - it'll be called if BOM character was actually found.\n * If you want to detect UTF-8 BOM when decoding other encodings, use [node-autodetect-decoder-stream](https://github.com/danielgindi/node-autodetect-decoder-stream) module.\n * Encoding: No BOM added, unless overridden by `addBOM: true` option.\n\n## UTF-16 Encodings\n\nThis library supports UTF-16LE, UTF-16BE and UTF-16 encodings. First two are straightforward, but UTF-16 is trying to be\nsmart about endianness in the following ways:\n * Decoding: uses BOM and 'spaces heuristic' to determine input endianness. Default is UTF-16LE, but can be \n overridden with `defaultEncoding: 'utf-16be'` option. Strips BOM unless `stripBOM: false`.\n * Encoding: uses UTF-16LE and writes BOM by default. Use `addBOM: false` to override.\n\n## Other notes\n\nWhen decoding, be sure to supply a Buffer to decode() method, otherwise [bad things usually happen](https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding). \nUntranslatable characters are set to � or ?. No transliteration is currently supported. \nNode versions 0.10.31 and 0.11.13 are buggy, don't use them (see #65, #77). \n\n## Testing\n\n```bash\n$ git clone git@github.com:ashtuchkin/iconv-lite.git\n$ cd iconv-lite\n$ npm install\n$ npm test\n \n$ # To view performance:\n$ node test/performance.js\n\n$ # To view test coverage:\n$ npm run coverage\n$ open coverage/lcov-report/index.html\n```\n\n## Adoption\n[![NPM](https://nodei.co/npm-dl/iconv-lite.png)](https://nodei.co/npm/iconv-lite/)\n[![Codeship Status for ashtuchkin/iconv-lite](https://www.codeship.com/projects/81670840-fa72-0131-4520-4a01a6c01acc/status)](https://www.codeship.com/projects/29053)\n", | ||
@@ -153,0 +120,0 @@ "readmeFilename": "README.md", |
@@ -8,34 +8,30 @@ | ||
## v2.0.0 | ||
## v2.0.0-alpha.3 | ||
This is a major release. Check [our upgrade guide](https://github.com/bitinn/node-fetch/blob/master/UPGRADE-GUIDE.md) for an overview on some key differences between v1 and v2. | ||
- Major: overwrite user's `Content-Length` if we can be sure our information is correct | ||
- Fix: exhaust list in `Headers` constructor before processing | ||
### General changes | ||
## v2.0.0-alpha.2 | ||
- Major: Node.js 0.10.x and 0.12.x support is dropped | ||
- Major: `require('node-fetch/lib/response')` etc. is now unsupported; use `require('node-fetch').Response` or ES6 module imports | ||
- Enhance: start testing on Node.js 4, 6, 7 | ||
- Enhance: use Rollup to produce a distributed bundle (less memory overhead and faster startup) | ||
- Enhance: make `Object.prototype.toString()` on Headers, Requests, and Responses return correct class strings | ||
- Other: rewrite in ES2015 using Babel | ||
- Other: use Codecov for code coverage tracking | ||
- Major: remove `headers.getAll()`; make `get()` return all headers delimited by commas (per spec) | ||
- Major: remove undocumented `FOLLOW_SPEC` switch -- it is now the default | ||
### HTTP requests | ||
## v2.0.0-alpha.1 | ||
- Major: overwrite user's `Content-Length` if we can be sure our information is correct (per spec) | ||
- Fix: support WHATWG URL objects, created by `whatwg-url` package or `require('url').URL` in Node.js 7+ | ||
This is a major release. Check [our upgrade guide](https://github.com/bitinn/node-fetch/blob/master/UPGRADE-GUIDE.md) for an overview on some key differences between v1 and v2. | ||
### Response and Request classes | ||
- Major: `response.text()` no longer attempts to detect encoding, instead always opting for UTF-8 (per spec); use `response.textConverted()` for the v1 behavior | ||
- Major: make `response.json()` throw error instead of returning an empty object on 204 no-content respose (per spec; reverts behavior changed in v1.6.2) | ||
- Major: Node.js 0.10.x support is dropped | ||
- Major: rewrite in transpiled ES2015 | ||
- Major: internal methods are no longer exposed | ||
- Major: throw error when a `GET` or `HEAD` Request is constructed with a non-null body (per spec) | ||
- Major: throw error when a GET/HEAD Request is constructed with a non-null body (per spec) | ||
- Major: `response.text()` no longer attempts to detect encoding, instead always opting for UTF-8 (per spec); use `response.textConverted()` for the old behavior | ||
- Major: make `response.json()` throw error instead of returning an empty object on 204 no-content respose (per spec; reverts behavior set in v1.6.2) | ||
- Major: arrays as parameters to `headers.append` and `headers.set` are joined as a string (per spec) | ||
- Enhance: start testing on Node.js 4, 6, 7 | ||
- Enhance: use Rollup to produce a distributed bundle (less memory overhead and faster startup) | ||
- Enhance: make `toString()` on Headers, Requests, and Responses return correct IDL class strings | ||
- Enhance: add an option to conform to latest spec at the expense of reduced compatibility | ||
- Enhance: set `Content-Length` header for Buffers as well | ||
- Enhance: add `response.arrayBuffer()` (also applies to Requests) | ||
- Enhance: add experimental `response.blob()` (also applies to Requests) | ||
- Fix: fix Request and Response with `null` body | ||
### Headers class | ||
- Major: remove `headers.getAll()`; make `get()` return all headers delimited by commas (per spec) | ||
- Enhance: make Headers iterable | ||
@@ -45,9 +41,7 @@ - Enhance: make Headers constructor accept an array of tuples | ||
- Fix: coerce Headers prototype function parameters to strings, where applicable | ||
- Fix: fix Request and Response with `null` body | ||
- Fix: support WHATWG URL objects, created by `whatwg-url` package or `require('url').URL` in Node.js 7+ | ||
- Other: use Codecov for code coverage tracking | ||
### Documentation | ||
- Enhance: more comprehensive API docs | ||
- Enhance: add a list of default headers in README | ||
# 1.x release | ||
@@ -54,0 +48,0 @@ |
@@ -0,8 +1,26 @@ | ||
import _getIterator from 'babel-runtime/core-js/get-iterator'; | ||
import _Object$keys from 'babel-runtime/core-js/object/keys'; | ||
import { format, parse, resolve } from 'url'; | ||
import { STATUS_CODES } from 'http'; | ||
import * as http from 'http'; | ||
import { Z_SYNC_FLUSH, createGunzip, createInflate, createInflateRaw } from 'zlib'; | ||
import * as https from 'https'; | ||
import { createGunzip, createInflate, createInflateRaw } from 'zlib'; | ||
import * as zlib from 'zlib'; | ||
import Stream, { PassThrough } from 'stream'; | ||
import { PassThrough } from 'stream'; | ||
import _Object$assign from 'babel-runtime/core-js/object/assign'; | ||
import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; | ||
import _createClass from 'babel-runtime/helpers/createClass'; | ||
import _Symbol from 'babel-runtime/core-js/symbol'; | ||
import { convert } from 'encoding'; | ||
import bodyStream from 'is-stream'; | ||
import toArrayBuffer from 'buffer-to-arraybuffer'; | ||
import _Symbol$toStringTag from 'babel-runtime/core-js/symbol/to-string-tag'; | ||
import _Object$defineProperty from 'babel-runtime/core-js/object/define-property'; | ||
import _Object$create from 'babel-runtime/core-js/object/create'; | ||
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; | ||
import _inherits from 'babel-runtime/helpers/inherits'; | ||
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; | ||
import _Object$setPrototypeOf from 'babel-runtime/core-js/object/set-prototype-of'; | ||
import _Array$from from 'babel-runtime/core-js/array/from'; | ||
import _Symbol$iterator from 'babel-runtime/core-js/symbol/iterator'; | ||
@@ -12,9 +30,11 @@ // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js | ||
const BUFFER = Symbol('buffer'); | ||
const TYPE = Symbol('type'); | ||
const CLOSED = Symbol('closed'); | ||
var BUFFER = _Symbol('buffer'); | ||
var TYPE = _Symbol('type'); | ||
var CLOSED = _Symbol('closed'); | ||
class Blob { | ||
constructor() { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
var Blob = function () { | ||
function Blob() { | ||
_classCallCheck(this, Blob); | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
value: 'Blob', | ||
@@ -29,13 +49,13 @@ writable: false, | ||
const blobParts = arguments[0]; | ||
const options = arguments[1]; | ||
var blobParts = arguments[0]; | ||
var options = arguments[1]; | ||
const buffers = []; | ||
var buffers = []; | ||
if (blobParts) { | ||
const a = blobParts; | ||
const length = Number(a.length); | ||
for (let i = 0; i < length; i++) { | ||
const element = a[i]; | ||
let buffer; | ||
var a = blobParts; | ||
var length = Number(a.length); | ||
for (var i = 0; i < length; i++) { | ||
var element = a[i]; | ||
var buffer = void 0; | ||
if (element instanceof Buffer) { | ||
@@ -58,3 +78,3 @@ buffer = element; | ||
let type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
var type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
if (type && !/[^\u0020-\u007E]/.test(type)) { | ||
@@ -64,17 +84,10 @@ this[TYPE] = type; | ||
} | ||
get size() { | ||
return this[CLOSED] ? 0 : this[BUFFER].length; | ||
} | ||
get type() { | ||
return this[TYPE]; | ||
} | ||
get isClosed() { | ||
return this[CLOSED]; | ||
} | ||
slice() { | ||
const size = this.size; | ||
const start = arguments[0]; | ||
const end = arguments[1]; | ||
let relativeStart, relativeEnd; | ||
Blob.prototype.slice = function slice() { | ||
var size = this.size; | ||
var start = arguments[0]; | ||
var end = arguments[1]; | ||
var relativeStart = void 0, | ||
relativeEnd = void 0; | ||
if (start === undefined) { | ||
@@ -94,17 +107,37 @@ relativeStart = 0; | ||
} | ||
const span = Math.max(relativeEnd - relativeStart, 0); | ||
var span = Math.max(relativeEnd - relativeStart, 0); | ||
const buffer = this[BUFFER]; | ||
const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
const blob = new Blob([], { type: arguments[2] }); | ||
var buffer = this[BUFFER]; | ||
var slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
var blob = new Blob([], { type: arguments[2] }); | ||
blob[BUFFER] = slicedBuffer; | ||
blob[CLOSED] = this[CLOSED]; | ||
return blob; | ||
} | ||
close() { | ||
}; | ||
Blob.prototype.close = function close() { | ||
this[CLOSED] = true; | ||
} | ||
} | ||
}; | ||
Object.defineProperty(Blob.prototype, Symbol.toStringTag, { | ||
_createClass(Blob, [{ | ||
key: 'size', | ||
get: function get() { | ||
return this[CLOSED] ? 0 : this[BUFFER].length; | ||
} | ||
}, { | ||
key: 'type', | ||
get: function get() { | ||
return this[TYPE]; | ||
} | ||
}, { | ||
key: 'isClosed', | ||
get: function get() { | ||
return this[CLOSED]; | ||
} | ||
}]); | ||
return Blob; | ||
}(); | ||
_Object$defineProperty(Blob.prototype, _Symbol$toStringTag, { | ||
value: 'BlobPrototype', | ||
@@ -145,3 +178,3 @@ writable: false, | ||
FetchError.prototype = Object.create(Error.prototype); | ||
FetchError.prototype = _Object$create(Error.prototype); | ||
FetchError.prototype.constructor = FetchError; | ||
@@ -156,3 +189,4 @@ FetchError.prototype.name = 'FetchError'; | ||
const DISTURBED = Symbol('disturbed'); | ||
var DISTURBED = _Symbol('disturbed'); | ||
var CONSUME_BODY = _Symbol('consumeBody'); | ||
@@ -162,4 +196,2 @@ /** | ||
* | ||
* Cannot use ES6 class because Body must be called with .call(). | ||
* | ||
* @param Stream body Readable stream | ||
@@ -169,37 +201,35 @@ * @param Object opts Response options | ||
*/ | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size; | ||
let size = _ref$size === undefined ? 0 : _ref$size; | ||
var _ref$timeout = _ref.timeout; | ||
let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
var Body = function () { | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size, | ||
size = _ref$size === undefined ? 0 : _ref$size, | ||
_ref$timeout = _ref.timeout, | ||
timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
if (body == null) { | ||
// body is undefined or null | ||
body = null; | ||
} else if (typeof body === 'string') { | ||
// body is string | ||
} else if (body instanceof Blob) { | ||
// body is blob | ||
} else if (Buffer.isBuffer(body)) { | ||
// body is buffer | ||
} else if (body instanceof Stream) { | ||
// body is stream | ||
} else { | ||
// none of the above | ||
// coerce to string | ||
body = String(body); | ||
_classCallCheck(this, Body); | ||
if (body == null) { | ||
// body is undefined or null | ||
body = null; | ||
} else if (typeof body === 'string') { | ||
// body is string | ||
} else if (body instanceof Blob) { | ||
// body is blob | ||
} else if (Buffer.isBuffer(body)) { | ||
// body is buffer | ||
} else if (bodyStream(body)) { | ||
// body is stream | ||
} else { | ||
// none of the above | ||
// coerce to string | ||
body = String(body); | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
Body.prototype = { | ||
get bodyUsed() { | ||
return this[DISTURBED]; | ||
}, | ||
/** | ||
@@ -210,7 +240,7 @@ * Decode response as ArrayBuffer | ||
*/ | ||
arrayBuffer() { | ||
return consumeBody.call(this).then(function (buf) { | ||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); | ||
Body.prototype.arrayBuffer = function arrayBuffer() { | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
return toArrayBuffer(buf); | ||
}); | ||
}, | ||
}; | ||
@@ -222,14 +252,16 @@ /** | ||
*/ | ||
blob() { | ||
let ct = this.headers && this.headers.get('content-type') || ''; | ||
return consumeBody.call(this).then(function (buf) { | ||
return Object.assign( | ||
Body.prototype.blob = function blob() { | ||
var ct = this.headers && this.headers.get('content-type') || ''; | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
var _Object$assign2; | ||
return _Object$assign( | ||
// Prevent copying | ||
new Blob([], { | ||
type: ct.toLowerCase() | ||
}), { | ||
[BUFFER]: buf | ||
}); | ||
}), (_Object$assign2 = {}, _Object$assign2[BUFFER] = buf, _Object$assign2)); | ||
}); | ||
}, | ||
}; | ||
@@ -241,7 +273,9 @@ /** | ||
*/ | ||
json() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
Body.prototype.json = function json() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return JSON.parse(buffer.toString()); | ||
}); | ||
}, | ||
}; | ||
@@ -253,7 +287,9 @@ /** | ||
*/ | ||
text() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
Body.prototype.text = function text() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return buffer.toString(); | ||
}); | ||
}, | ||
}; | ||
@@ -265,6 +301,8 @@ /** | ||
*/ | ||
buffer() { | ||
return consumeBody.call(this); | ||
}, | ||
Body.prototype.buffer = function buffer() { | ||
return this[CONSUME_BODY](); | ||
}; | ||
/** | ||
@@ -276,121 +314,116 @@ * Decode response as text, while automatically detecting the encoding and | ||
*/ | ||
textConverted() { | ||
Body.prototype.textConverted = function textConverted() { | ||
var _this = this; | ||
return consumeBody.call(this).then(function (buffer) { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return convertBody(buffer, _this.headers); | ||
}); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
Body.mixIn = function (proto) { | ||
for (const name of Object.getOwnPropertyNames(Body.prototype)) { | ||
// istanbul ignore else: future proof | ||
if (!(name in proto)) { | ||
const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); | ||
Object.defineProperty(proto, name, desc); | ||
Body.prototype[CONSUME_BODY] = function () { | ||
var _this2 = this; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error('body used already for: ' + this.url)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
function consumeBody(body) { | ||
var _this2 = this; | ||
this[DISTURBED] = true; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error(`body used already for: ${this.url}`)); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
this[DISTURBED] = true; | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// istanbul ignore if: should never happen | ||
if (!bodyStream(this.body)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
var accum = []; | ||
var accumBytes = 0; | ||
var abort = false; | ||
// istanbul ignore if: should never happen | ||
if (!(this.body instanceof Stream)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
var resTimeout = void 0; | ||
// body is stream | ||
// get ready to actually consume the body | ||
let accum = []; | ||
let accumBytes = 0; | ||
let abort = false; | ||
// allow timeout on slow response body | ||
if (_this2.timeout) { | ||
resTimeout = setTimeout(function () { | ||
abort = true; | ||
reject(new FetchError('Response timeout while trying to fetch ' + _this2.url + ' (over ' + _this2.timeout + 'ms)', 'body-timeout')); | ||
}, _this2.timeout); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
let resTimeout; | ||
// handle stream error, such as incorrect content-encoding | ||
_this2.body.on('error', function (err) { | ||
reject(new FetchError('Invalid response body while trying to fetch ' + _this2.url + ': ' + err.message, 'system', err)); | ||
}); | ||
// allow timeout on slow response body | ||
if (_this2.timeout) { | ||
resTimeout = setTimeout(function () { | ||
abort = true; | ||
reject(new FetchError(`Response timeout while trying to fetch ${_this2.url} (over ${_this2.timeout}ms)`, 'body-timeout')); | ||
}, _this2.timeout); | ||
} | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
// handle stream error, such as incorrect content-encoding | ||
_this2.body.on('error', function (err) { | ||
reject(new FetchError(`Invalid response body while trying to fetch ${_this2.url}: ${err.message}`, 'system', err)); | ||
}); | ||
if (_this2.size && accumBytes + chunk.length > _this2.size) { | ||
abort = true; | ||
reject(new FetchError('content size at ' + _this2.url + ' over limit: ' + _this2.size, 'max-size')); | ||
return; | ||
} | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
if (_this2.size && accumBytes + chunk.length > _this2.size) { | ||
abort = true; | ||
reject(new FetchError(`content size at ${_this2.url} over limit: ${_this2.size}`, 'max-size')); | ||
return; | ||
} | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
}; | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
_createClass(Body, [{ | ||
key: 'bodyUsed', | ||
get: function get() { | ||
return this[DISTURBED]; | ||
} | ||
}]); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
} | ||
return Body; | ||
}(); | ||
/** | ||
* Detect buffer encoding and convert to target encoding | ||
* ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding | ||
* | ||
* @param Buffer buffer Incoming buffer | ||
* @param String encoding Target encoding | ||
* @return String | ||
*/ | ||
function convertBody(buffer, headers) { | ||
const ct = headers.get('content-type'); | ||
let charset = 'utf-8'; | ||
let res, str; | ||
var ct = headers.get('content-type'); | ||
var charset = 'utf-8'; | ||
var res = void 0, | ||
str = void 0; | ||
@@ -445,5 +478,6 @@ // header | ||
*/ | ||
function clone(instance) { | ||
let p1, p2; | ||
let body = instance.body; | ||
function clone$1(instance) { | ||
var p1 = void 0, | ||
p2 = void 0; | ||
var body = instance.body; | ||
@@ -457,3 +491,3 @@ // don't allow cloning a used body | ||
// note: we can't clone the form-data object without having it as a dependency | ||
if (body instanceof Stream && typeof body.getBoundary !== 'function') { | ||
if (bodyStream(body) && typeof body.getBoundary !== 'function') { | ||
// tee instance body | ||
@@ -482,3 +516,3 @@ p1 = new PassThrough(); | ||
function extractContentType(instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
@@ -502,3 +536,3 @@ // istanbul ignore if: Currently, because of a guard in Request, body | ||
// detect form data input from form-data module | ||
return `multipart/form-data;boundary=${body.getBoundary()}`; | ||
return 'multipart/form-data;boundary=' + body.getBoundary(); | ||
} else { | ||
@@ -512,5 +546,4 @@ // body is stream | ||
function getTotalBytes(instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
// istanbul ignore if: included for completion | ||
@@ -545,3 +578,3 @@ if (body === null) { | ||
function writeToStream(dest, instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
@@ -618,3 +651,3 @@ | ||
if (!isValidTokenChar(val.charCodeAt(0))) return false; | ||
const len = val.length; | ||
var len = val.length; | ||
if (len > 1) { | ||
@@ -672,3 +705,3 @@ if (!isValidTokenChar(val.charCodeAt(1))) return false; | ||
if (!checkIsHttpToken(name)) { | ||
throw new TypeError(`${name} is not a legal HTTP header name`); | ||
throw new TypeError(name + ' is not a legal HTTP header name'); | ||
} | ||
@@ -681,3 +714,3 @@ return name.toLowerCase(); | ||
if (checkInvalidHeaderChar(value)) { | ||
throw new TypeError(`${value} is not a legal HTTP header value`); | ||
throw new TypeError(value + ' is not a legal HTTP header value'); | ||
} | ||
@@ -687,4 +720,5 @@ return value; | ||
const MAP = Symbol('map'); | ||
class Headers { | ||
var MAP = _Symbol('map'); | ||
var Headers = function () { | ||
/** | ||
@@ -696,20 +730,9 @@ * Headers class | ||
*/ | ||
constructor() { | ||
let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
function Headers() { | ||
var init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
this[MAP] = Object.create(null); | ||
_classCallCheck(this, Headers); | ||
if (init instanceof Headers) { | ||
const rawHeaders = init.raw(); | ||
const headerNames = Object.keys(rawHeaders); | ||
this[MAP] = _Object$create(null); | ||
for (const headerName of headerNames) { | ||
for (const value of rawHeaders[headerName]) { | ||
this.append(headerName, value); | ||
} | ||
} | ||
return; | ||
} | ||
// We don't worry about converting prop to ByteString here as append() | ||
@@ -720,3 +743,3 @@ // will handle it. | ||
} else if (typeof init === 'object') { | ||
const method = init[Symbol.iterator]; | ||
var method = init[_Symbol$iterator]; | ||
if (method != null) { | ||
@@ -729,20 +752,59 @@ if (typeof method !== 'function') { | ||
// 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') { | ||
var pairs = []; | ||
for (var _iterator = init, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var pair = _ref; | ||
if (typeof pair !== 'object' || typeof pair[_Symbol$iterator] !== 'function') { | ||
throw new TypeError('Each header pair must be iterable'); | ||
} | ||
pairs.push(Array.from(pair)); | ||
pairs.push(_Array$from(pair)); | ||
} | ||
for (const pair of pairs) { | ||
if (pair.length !== 2) { | ||
for (var _iterator2 = pairs, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) { | ||
var _ref2; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref2 = _i2.value; | ||
} | ||
var _pair = _ref2; | ||
if (_pair.length !== 2) { | ||
throw new TypeError('Each header pair must be a name/value tuple'); | ||
} | ||
this.append(pair[0], pair[1]); | ||
this.append(_pair[0], _pair[1]); | ||
} | ||
} else { | ||
// record<ByteString, ByteString> | ||
for (const key of Object.keys(init)) { | ||
const value = init[key]; | ||
for (var _iterator3 = _Object$keys(init), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _getIterator(_iterator3);;) { | ||
var _ref3; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref3 = _iterator3[_i3++]; | ||
} else { | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref3 = _i3.value; | ||
} | ||
var key = _ref3; | ||
var value = init[key]; | ||
this.append(key, value); | ||
@@ -755,3 +817,3 @@ } | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
value: 'Headers', | ||
@@ -770,4 +832,6 @@ writable: false, | ||
*/ | ||
get(name) { | ||
const list = this[MAP][sanitizeName(name)]; | ||
Headers.prototype.get = function get(name) { | ||
var list = this[MAP][sanitizeName(name)]; | ||
if (!list) { | ||
@@ -777,4 +841,4 @@ return null; | ||
return list.join(', '); | ||
} | ||
return list.join(','); | ||
}; | ||
@@ -788,11 +852,13 @@ /** | ||
*/ | ||
forEach(callback) { | ||
let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
let pairs = getHeaderPairs(this); | ||
let i = 0; | ||
Headers.prototype.forEach = function forEach(callback) { | ||
var thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
var pairs = getHeaderPairs(this); | ||
var i = 0; | ||
while (i < pairs.length) { | ||
var _pairs$i = pairs[i]; | ||
const name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
var _pairs$i = pairs[i], | ||
name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
@@ -803,3 +869,3 @@ callback.call(thisArg, value, name, this); | ||
} | ||
} | ||
}; | ||
@@ -813,5 +879,7 @@ /** | ||
*/ | ||
set(name, value) { | ||
Headers.prototype.set = function set(name, value) { | ||
this[MAP][sanitizeName(name)] = [sanitizeValue(value)]; | ||
} | ||
}; | ||
@@ -825,3 +893,5 @@ /** | ||
*/ | ||
append(name, value) { | ||
Headers.prototype.append = function append(name, value) { | ||
if (!this.has(name)) { | ||
@@ -833,3 +903,3 @@ this.set(name, value); | ||
this[MAP][sanitizeName(name)].push(sanitizeValue(value)); | ||
} | ||
}; | ||
@@ -842,5 +912,7 @@ /** | ||
*/ | ||
has(name) { | ||
Headers.prototype.has = function has(name) { | ||
return !!this[MAP][sanitizeName(name)]; | ||
} | ||
}; | ||
@@ -853,5 +925,7 @@ /** | ||
*/ | ||
delete(name) { | ||
Headers.prototype.delete = function _delete(name) { | ||
delete this[MAP][sanitizeName(name)]; | ||
} | ||
}; | ||
@@ -863,5 +937,5 @@ /** | ||
*/ | ||
raw() { | ||
Headers.prototype.raw = function raw() { | ||
return this[MAP]; | ||
} | ||
}; | ||
@@ -873,5 +947,7 @@ /** | ||
*/ | ||
keys() { | ||
Headers.prototype.keys = function keys() { | ||
return createHeadersIterator(this, 'key'); | ||
} | ||
}; | ||
@@ -883,5 +959,7 @@ /** | ||
*/ | ||
values() { | ||
Headers.prototype.values = function values() { | ||
return createHeadersIterator(this, 'value'); | ||
} | ||
}; | ||
@@ -895,9 +973,14 @@ /** | ||
*/ | ||
[Symbol.iterator]() { | ||
Headers.prototype[_Symbol$iterator] = function () { | ||
return createHeadersIterator(this, 'key+value'); | ||
} | ||
} | ||
Headers.prototype.entries = Headers.prototype[Symbol.iterator]; | ||
}; | ||
Object.defineProperty(Headers.prototype, Symbol.toStringTag, { | ||
return Headers; | ||
}(); | ||
Headers.prototype.entries = Headers.prototype[_Symbol$iterator]; | ||
_Object$defineProperty(Headers.prototype, _Symbol$toStringTag, { | ||
value: 'HeadersPrototype', | ||
@@ -910,3 +993,3 @@ writable: false, | ||
function getHeaderPairs(headers, kind) { | ||
const keys = Object.keys(headers[MAP]).sort(); | ||
var keys = _Object$keys(headers[MAP]).sort(); | ||
return keys.map(kind === 'key' ? function (k) { | ||
@@ -919,9 +1002,9 @@ return [k]; | ||
const INTERNAL = Symbol('internal'); | ||
var INTERNAL = _Symbol('internal'); | ||
function createHeadersIterator(target, kind) { | ||
const iterator = Object.create(HeadersIteratorPrototype); | ||
var iterator = _Object$create(HeadersIteratorPrototype); | ||
iterator[INTERNAL] = { | ||
target, | ||
kind, | ||
target: target, | ||
kind: kind, | ||
index: 0 | ||
@@ -932,16 +1015,16 @@ }; | ||
const HeadersIteratorPrototype = Object.setPrototypeOf({ | ||
next() { | ||
var HeadersIteratorPrototype = _Object$setPrototypeOf({ | ||
next: function next() { | ||
// istanbul ignore if | ||
if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { | ||
if (!this || _Object$getPrototypeOf(this) !== HeadersIteratorPrototype) { | ||
throw new TypeError('Value of `this` is not a HeadersIterator'); | ||
} | ||
var _INTERNAL = this[INTERNAL]; | ||
const target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var _INTERNAL = this[INTERNAL], | ||
target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
const values = getHeaderPairs(target, kind); | ||
const len = values.length; | ||
var values = getHeaderPairs(target, kind); | ||
var len = values.length; | ||
if (index >= len) { | ||
@@ -954,6 +1037,6 @@ return { | ||
const pair = values[index]; | ||
var pair = values[index]; | ||
this[INTERNAL].index = index + 1; | ||
let result; | ||
var result = void 0; | ||
if (kind === 'key') { | ||
@@ -972,5 +1055,12 @@ result = pair[0]; | ||
} | ||
}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); | ||
}, _Object$getPrototypeOf(_Object$getPrototypeOf(_getIterator([])))); | ||
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { | ||
// On Node.js v0.12 the %IteratorPrototype% object is broken | ||
if (typeof HeadersIteratorPrototype[_Symbol$iterator] !== 'function') { | ||
HeadersIteratorPrototype[_Symbol$iterator] = function () { | ||
return this; | ||
}; | ||
} | ||
_Object$defineProperty(HeadersIteratorPrototype, _Symbol$toStringTag, { | ||
value: 'HeadersIterator', | ||
@@ -995,16 +1085,20 @@ writable: false, | ||
*/ | ||
class Response { | ||
constructor() { | ||
let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
Body.call(this, body, opts); | ||
var Response = function (_Body) { | ||
_inherits(Response, _Body); | ||
this.url = opts.url; | ||
this.status = opts.status || 200; | ||
this.statusText = opts.statusText || STATUS_CODES[this.status]; | ||
function Response() { | ||
var body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.headers = new Headers(opts.headers); | ||
_classCallCheck(this, Response); | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, body, opts)); | ||
_this.url = opts.url; | ||
_this.status = opts.status || 200; | ||
_this.statusText = opts.statusText || STATUS_CODES[_this.status]; | ||
_this.headers = new Headers(opts.headers); | ||
_Object$defineProperty(_this, _Symbol$toStringTag, { | ||
value: 'Response', | ||
@@ -1015,2 +1109,3 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
@@ -1021,6 +1116,4 @@ | ||
*/ | ||
get ok() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
/** | ||
@@ -1031,5 +1124,5 @@ * Clone this response | ||
*/ | ||
clone() { | ||
Response.prototype.clone = function clone() { | ||
return new Response(clone(this), { | ||
return new Response(clone$1(this), { | ||
url: this.url, | ||
@@ -1041,8 +1134,15 @@ status: this.status, | ||
}); | ||
} | ||
} | ||
}; | ||
Body.mixIn(Response.prototype); | ||
_createClass(Response, [{ | ||
key: 'ok', | ||
get: function get() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
}]); | ||
Object.defineProperty(Response.prototype, Symbol.toStringTag, { | ||
return Response; | ||
}(Body); | ||
_Object$defineProperty(Response.prototype, _Symbol$toStringTag, { | ||
value: 'ResponsePrototype', | ||
@@ -1060,3 +1160,3 @@ writable: false, | ||
const PARSED_URL = Symbol('url'); | ||
var PARSED_URL = _Symbol('url'); | ||
@@ -1070,8 +1170,13 @@ /** | ||
*/ | ||
class Request { | ||
constructor(input) { | ||
let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
let parsedURL; | ||
var Request = function (_Body) { | ||
_inherits(Request, _Body); | ||
function Request(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, Request); | ||
var parsedURL = void 0; | ||
// normalize input | ||
@@ -1086,3 +1191,3 @@ if (!(input instanceof Request)) { | ||
// coerce input to a string before attempting to parse | ||
parsedURL = parse(`${input}`); | ||
parsedURL = parse('' + input); | ||
} | ||
@@ -1094,3 +1199,3 @@ input = {}; | ||
let method = init.method || input.method || 'GET'; | ||
var method = init.method || input.method || 'GET'; | ||
@@ -1101,18 +1206,18 @@ if ((init.body != null || input instanceof Request && input.body !== null) && (method === 'GET' || method === 'HEAD')) { | ||
let inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone(input) : null; | ||
var inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone$1(input) : null; | ||
Body.call(this, inputBody, { | ||
// fetch spec options | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, inputBody, { | ||
timeout: init.timeout || input.timeout || 0, | ||
size: init.size || input.size || 0 | ||
}); | ||
})); | ||
// fetch spec options | ||
this.method = method.toUpperCase(); | ||
this.redirect = init.redirect || input.redirect || 'follow'; | ||
this.headers = new Headers(init.headers || input.headers || {}); | ||
_this.method = method.toUpperCase(); | ||
_this.redirect = init.redirect || input.redirect || 'follow'; | ||
_this.headers = new Headers(init.headers || input.headers || {}); | ||
if (init.body != null) { | ||
const contentType = extractContentType(this); | ||
if (contentType !== null && !this.headers.has('Content-Type')) { | ||
this.headers.append('Content-Type', contentType); | ||
var contentType = extractContentType(_this); | ||
if (contentType !== null && !_this.headers.has('Content-Type')) { | ||
_this.headers.append('Content-Type', contentType); | ||
} | ||
@@ -1122,9 +1227,9 @@ } | ||
// server 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.counter = init.counter || input.counter || 0; | ||
this.agent = init.agent || input.agent; | ||
_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.counter = init.counter || input.counter || 0; | ||
_this.agent = init.agent || input.agent; | ||
this[PARSED_URL] = parsedURL; | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
_this[PARSED_URL] = parsedURL; | ||
_Object$defineProperty(_this, _Symbol$toStringTag, { | ||
value: 'Request', | ||
@@ -1135,8 +1240,5 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
get url() { | ||
return format(this[PARSED_URL]); | ||
} | ||
/** | ||
@@ -1147,10 +1249,17 @@ * Clone this request | ||
*/ | ||
clone() { | ||
Request.prototype.clone = function clone$1() { | ||
return new Request(this); | ||
} | ||
} | ||
}; | ||
Body.mixIn(Request.prototype); | ||
_createClass(Request, [{ | ||
key: 'url', | ||
get: function get() { | ||
return format(this[PARSED_URL]); | ||
} | ||
}]); | ||
Object.defineProperty(Request.prototype, Symbol.toStringTag, { | ||
return Request; | ||
}(Body); | ||
_Object$defineProperty(Request.prototype, _Symbol$toStringTag, { | ||
value: 'RequestPrototype', | ||
@@ -1163,4 +1272,3 @@ writable: false, | ||
function getNodeRequestOptions(request) { | ||
const parsedURL = request[PARSED_URL]; | ||
const headers = new Headers(request.headers); | ||
var headers = new Headers(request.headers); | ||
@@ -1173,12 +1281,8 @@ // fetch step 3 | ||
// Basic fetch | ||
if (!parsedURL.protocol || !parsedURL.hostname) { | ||
throw new TypeError('Only absolute URLs are supported'); | ||
if (!/^https?:$/.test(request[PARSED_URL].protocol)) { | ||
throw new Error('only http(s) protocols are supported'); | ||
} | ||
if (!/^https?:$/.test(parsedURL.protocol)) { | ||
throw new TypeError('Only HTTP(S) protocols are supported'); | ||
} | ||
// HTTP-network-or-cache fetch steps 5-9 | ||
let contentLengthValue = null; | ||
var contentLengthValue = null; | ||
if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { | ||
@@ -1188,3 +1292,3 @@ contentLengthValue = '0'; | ||
if (request.body != null) { | ||
const totalBytes = getTotalBytes(request); | ||
var totalBytes = getTotalBytes(request); | ||
if (typeof totalBytes === 'number') { | ||
@@ -1214,3 +1318,3 @@ contentLengthValue = String(totalBytes); | ||
return Object.assign({}, parsedURL, { | ||
return _Object$assign({}, request[PARSED_URL], { | ||
method: request.method, | ||
@@ -1247,7 +1351,12 @@ headers: headers.raw(), | ||
// build request object | ||
const request = new Request(url$$1, opts); | ||
const options = getNodeRequestOptions(request); | ||
var request = new Request(url$$1, opts); | ||
const send = (options.protocol === 'https:' ? https : http).request; | ||
var options = getNodeRequestOptions(request); | ||
if (!options.protocol || !options.hostname) { | ||
throw new Error('only absolute urls are supported'); | ||
} | ||
var send = (options.protocol === 'https:' ? https : http).request; | ||
// http.request only support string as host header, this hack make custom host header possible | ||
@@ -1259,4 +1368,4 @@ if (options.headers.host) { | ||
// send request | ||
const req = send(options); | ||
let reqTimeout; | ||
var req = send(options); | ||
var reqTimeout = void 0; | ||
@@ -1267,3 +1376,3 @@ if (request.timeout) { | ||
req.abort(); | ||
reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); | ||
reject(new FetchError('network timeout at: ' + request.url, 'request-timeout')); | ||
}, request.timeout); | ||
@@ -1275,3 +1384,3 @@ }); | ||
clearTimeout(reqTimeout); | ||
reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); | ||
reject(new FetchError('request to ' + request.url + ' failed, reason: ' + err.message, 'system', err)); | ||
}); | ||
@@ -1285,3 +1394,3 @@ | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
reject(new FetchError('redirect mode is set to error: ' + request.url, 'no-redirect')); | ||
return; | ||
@@ -1291,3 +1400,3 @@ } | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
reject(new FetchError('maximum redirect reached at: ' + request.url, 'max-redirect')); | ||
return; | ||
@@ -1297,3 +1406,3 @@ } | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
reject(new FetchError('redirect location header missing at: ' + request.url, 'invalid-redirect')); | ||
return; | ||
@@ -1316,10 +1425,36 @@ } | ||
// normalize location header for manual redirect mode | ||
const headers = new Headers(); | ||
for (const name of Object.keys(res.headers)) { | ||
if (Array.isArray(res.headers[name])) { | ||
for (const val of res.headers[name]) { | ||
headers.append(name, val); | ||
var headers = new Headers(); | ||
for (var _iterator = _Object$keys(res.headers), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _name = _ref; | ||
if (Array.isArray(res.headers[_name])) { | ||
for (var _iterator2 = res.headers[_name], _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) { | ||
var _ref2; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref2 = _i2.value; | ||
} | ||
var val = _ref2; | ||
headers.append(_name, val); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
headers.append(_name, res.headers[_name]); | ||
} | ||
@@ -1332,4 +1467,4 @@ } | ||
// prepare response | ||
let body = res.pipe(new PassThrough()); | ||
const response_options = { | ||
var body = res.pipe(new PassThrough()); | ||
var response_options = { | ||
url: request.url, | ||
@@ -1343,47 +1478,41 @@ status: res.statusCode, | ||
// HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); | ||
// response object | ||
var output = void 0; | ||
// HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. compression support is disabled | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 3. no content-encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { | ||
resolve$$1(new Response(body, response_options)); | ||
if (!request.compress || request.method === 'HEAD' || !headers.has('content-encoding') || res.statusCode === 204 || res.statusCode === 304) { | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
} | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
const zlibOptions = { | ||
flush: Z_SYNC_FLUSH, | ||
finishFlush: Z_SYNC_FLUSH | ||
}; | ||
// otherwise, check for gzip or deflate | ||
var name = headers.get('content-encoding'); | ||
// for gzip | ||
if (codings == 'gzip' || codings == 'x-gzip') { | ||
body = body.pipe(createGunzip(zlibOptions)); | ||
resolve$$1(new Response(body, response_options)); | ||
if (name == 'gzip' || name == 'x-gzip') { | ||
body = body.pipe(createGunzip()); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
} | ||
// for deflate | ||
if (codings == 'deflate' || codings == 'x-deflate') { | ||
// for deflate | ||
} else if (name == 'deflate' || name == 'x-deflate') { | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new PassThrough()); | ||
var raw = res.pipe(new PassThrough()); | ||
raw.once('data', function (chunk) { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(createInflate(zlibOptions)); | ||
body = body.pipe(createInflate()); | ||
} else { | ||
body = body.pipe(createInflateRaw(zlibOptions)); | ||
body = body.pipe(createInflateRaw()); | ||
} | ||
resolve$$1(new Response(body, response_options)); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
}); | ||
@@ -1394,3 +1523,5 @@ return; | ||
// otherwise, use response as-is | ||
resolve$$1(new Response(body, response_options)); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
}); | ||
@@ -1415,2 +1546,2 @@ | ||
export { Headers, Request, Response, FetchError };export default fetch; | ||
export { Headers, Request, Response };export default fetch; |
@@ -7,2 +7,4 @@ 'use strict'; | ||
var _getIterator = _interopDefault(require('babel-runtime/core-js/get-iterator')); | ||
var _Object$keys = _interopDefault(require('babel-runtime/core-js/object/keys')); | ||
var url = require('url'); | ||
@@ -12,5 +14,19 @@ var http = require('http'); | ||
var zlib = require('zlib'); | ||
var Stream = require('stream'); | ||
var Stream__default = _interopDefault(Stream); | ||
var stream = require('stream'); | ||
var _Object$assign = _interopDefault(require('babel-runtime/core-js/object/assign')); | ||
var _classCallCheck = _interopDefault(require('babel-runtime/helpers/classCallCheck')); | ||
var _createClass = _interopDefault(require('babel-runtime/helpers/createClass')); | ||
var _Symbol = _interopDefault(require('babel-runtime/core-js/symbol')); | ||
var encoding = require('encoding'); | ||
var bodyStream = _interopDefault(require('is-stream')); | ||
var toArrayBuffer = _interopDefault(require('buffer-to-arraybuffer')); | ||
var _Symbol$toStringTag = _interopDefault(require('babel-runtime/core-js/symbol/to-string-tag')); | ||
var _Object$defineProperty = _interopDefault(require('babel-runtime/core-js/object/define-property')); | ||
var _Object$create = _interopDefault(require('babel-runtime/core-js/object/create')); | ||
var _possibleConstructorReturn = _interopDefault(require('babel-runtime/helpers/possibleConstructorReturn')); | ||
var _inherits = _interopDefault(require('babel-runtime/helpers/inherits')); | ||
var _Object$getPrototypeOf = _interopDefault(require('babel-runtime/core-js/object/get-prototype-of')); | ||
var _Object$setPrototypeOf = _interopDefault(require('babel-runtime/core-js/object/set-prototype-of')); | ||
var _Array$from = _interopDefault(require('babel-runtime/core-js/array/from')); | ||
var _Symbol$iterator = _interopDefault(require('babel-runtime/core-js/symbol/iterator')); | ||
@@ -20,9 +36,11 @@ // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js | ||
const BUFFER = Symbol('buffer'); | ||
const TYPE = Symbol('type'); | ||
const CLOSED = Symbol('closed'); | ||
var BUFFER = _Symbol('buffer'); | ||
var TYPE = _Symbol('type'); | ||
var CLOSED = _Symbol('closed'); | ||
class Blob { | ||
constructor() { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
var Blob = function () { | ||
function Blob() { | ||
_classCallCheck(this, Blob); | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
value: 'Blob', | ||
@@ -37,13 +55,13 @@ writable: false, | ||
const blobParts = arguments[0]; | ||
const options = arguments[1]; | ||
var blobParts = arguments[0]; | ||
var options = arguments[1]; | ||
const buffers = []; | ||
var buffers = []; | ||
if (blobParts) { | ||
const a = blobParts; | ||
const length = Number(a.length); | ||
for (let i = 0; i < length; i++) { | ||
const element = a[i]; | ||
let buffer; | ||
var a = blobParts; | ||
var length = Number(a.length); | ||
for (var i = 0; i < length; i++) { | ||
var element = a[i]; | ||
var buffer = void 0; | ||
if (element instanceof Buffer) { | ||
@@ -66,3 +84,3 @@ buffer = element; | ||
let type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
var type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
if (type && !/[^\u0020-\u007E]/.test(type)) { | ||
@@ -72,17 +90,10 @@ this[TYPE] = type; | ||
} | ||
get size() { | ||
return this[CLOSED] ? 0 : this[BUFFER].length; | ||
} | ||
get type() { | ||
return this[TYPE]; | ||
} | ||
get isClosed() { | ||
return this[CLOSED]; | ||
} | ||
slice() { | ||
const size = this.size; | ||
const start = arguments[0]; | ||
const end = arguments[1]; | ||
let relativeStart, relativeEnd; | ||
Blob.prototype.slice = function slice() { | ||
var size = this.size; | ||
var start = arguments[0]; | ||
var end = arguments[1]; | ||
var relativeStart = void 0, | ||
relativeEnd = void 0; | ||
if (start === undefined) { | ||
@@ -102,17 +113,37 @@ relativeStart = 0; | ||
} | ||
const span = Math.max(relativeEnd - relativeStart, 0); | ||
var span = Math.max(relativeEnd - relativeStart, 0); | ||
const buffer = this[BUFFER]; | ||
const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
const blob = new Blob([], { type: arguments[2] }); | ||
var buffer = this[BUFFER]; | ||
var slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
var blob = new Blob([], { type: arguments[2] }); | ||
blob[BUFFER] = slicedBuffer; | ||
blob[CLOSED] = this[CLOSED]; | ||
return blob; | ||
} | ||
close() { | ||
}; | ||
Blob.prototype.close = function close() { | ||
this[CLOSED] = true; | ||
} | ||
} | ||
}; | ||
Object.defineProperty(Blob.prototype, Symbol.toStringTag, { | ||
_createClass(Blob, [{ | ||
key: 'size', | ||
get: function get() { | ||
return this[CLOSED] ? 0 : this[BUFFER].length; | ||
} | ||
}, { | ||
key: 'type', | ||
get: function get() { | ||
return this[TYPE]; | ||
} | ||
}, { | ||
key: 'isClosed', | ||
get: function get() { | ||
return this[CLOSED]; | ||
} | ||
}]); | ||
return Blob; | ||
}(); | ||
_Object$defineProperty(Blob.prototype, _Symbol$toStringTag, { | ||
value: 'BlobPrototype', | ||
@@ -153,3 +184,3 @@ writable: false, | ||
FetchError.prototype = Object.create(Error.prototype); | ||
FetchError.prototype = _Object$create(Error.prototype); | ||
FetchError.prototype.constructor = FetchError; | ||
@@ -164,3 +195,4 @@ FetchError.prototype.name = 'FetchError'; | ||
const DISTURBED = Symbol('disturbed'); | ||
var DISTURBED = _Symbol('disturbed'); | ||
var CONSUME_BODY = _Symbol('consumeBody'); | ||
@@ -170,4 +202,2 @@ /** | ||
* | ||
* Cannot use ES6 class because Body must be called with .call(). | ||
* | ||
* @param Stream body Readable stream | ||
@@ -177,37 +207,35 @@ * @param Object opts Response options | ||
*/ | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size; | ||
let size = _ref$size === undefined ? 0 : _ref$size; | ||
var _ref$timeout = _ref.timeout; | ||
let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
var Body = function () { | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size, | ||
size = _ref$size === undefined ? 0 : _ref$size, | ||
_ref$timeout = _ref.timeout, | ||
timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
if (body == null) { | ||
// body is undefined or null | ||
body = null; | ||
} else if (typeof body === 'string') { | ||
// body is string | ||
} else if (body instanceof Blob) { | ||
// body is blob | ||
} else if (Buffer.isBuffer(body)) { | ||
// body is buffer | ||
} else if (body instanceof Stream__default) { | ||
// body is stream | ||
} else { | ||
// none of the above | ||
// coerce to string | ||
body = String(body); | ||
_classCallCheck(this, Body); | ||
if (body == null) { | ||
// body is undefined or null | ||
body = null; | ||
} else if (typeof body === 'string') { | ||
// body is string | ||
} else if (body instanceof Blob) { | ||
// body is blob | ||
} else if (Buffer.isBuffer(body)) { | ||
// body is buffer | ||
} else if (bodyStream(body)) { | ||
// body is stream | ||
} else { | ||
// none of the above | ||
// coerce to string | ||
body = String(body); | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
Body.prototype = { | ||
get bodyUsed() { | ||
return this[DISTURBED]; | ||
}, | ||
/** | ||
@@ -218,7 +246,7 @@ * Decode response as ArrayBuffer | ||
*/ | ||
arrayBuffer() { | ||
return consumeBody.call(this).then(function (buf) { | ||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); | ||
Body.prototype.arrayBuffer = function arrayBuffer() { | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
return toArrayBuffer(buf); | ||
}); | ||
}, | ||
}; | ||
@@ -230,14 +258,16 @@ /** | ||
*/ | ||
blob() { | ||
let ct = this.headers && this.headers.get('content-type') || ''; | ||
return consumeBody.call(this).then(function (buf) { | ||
return Object.assign( | ||
Body.prototype.blob = function blob() { | ||
var ct = this.headers && this.headers.get('content-type') || ''; | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
var _Object$assign2; | ||
return _Object$assign( | ||
// Prevent copying | ||
new Blob([], { | ||
type: ct.toLowerCase() | ||
}), { | ||
[BUFFER]: buf | ||
}); | ||
}), (_Object$assign2 = {}, _Object$assign2[BUFFER] = buf, _Object$assign2)); | ||
}); | ||
}, | ||
}; | ||
@@ -249,7 +279,9 @@ /** | ||
*/ | ||
json() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
Body.prototype.json = function json() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return JSON.parse(buffer.toString()); | ||
}); | ||
}, | ||
}; | ||
@@ -261,7 +293,9 @@ /** | ||
*/ | ||
text() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
Body.prototype.text = function text() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return buffer.toString(); | ||
}); | ||
}, | ||
}; | ||
@@ -273,6 +307,8 @@ /** | ||
*/ | ||
buffer() { | ||
return consumeBody.call(this); | ||
}, | ||
Body.prototype.buffer = function buffer() { | ||
return this[CONSUME_BODY](); | ||
}; | ||
/** | ||
@@ -284,121 +320,116 @@ * Decode response as text, while automatically detecting the encoding and | ||
*/ | ||
textConverted() { | ||
Body.prototype.textConverted = function textConverted() { | ||
var _this = this; | ||
return consumeBody.call(this).then(function (buffer) { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return convertBody(buffer, _this.headers); | ||
}); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
Body.mixIn = function (proto) { | ||
for (const name of Object.getOwnPropertyNames(Body.prototype)) { | ||
// istanbul ignore else: future proof | ||
if (!(name in proto)) { | ||
const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); | ||
Object.defineProperty(proto, name, desc); | ||
Body.prototype[CONSUME_BODY] = function () { | ||
var _this2 = this; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error('body used already for: ' + this.url)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
function consumeBody(body) { | ||
var _this2 = this; | ||
this[DISTURBED] = true; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error(`body used already for: ${this.url}`)); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
this[DISTURBED] = true; | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// istanbul ignore if: should never happen | ||
if (!bodyStream(this.body)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
var accum = []; | ||
var accumBytes = 0; | ||
var abort = false; | ||
// istanbul ignore if: should never happen | ||
if (!(this.body instanceof Stream__default)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
var resTimeout = void 0; | ||
// body is stream | ||
// get ready to actually consume the body | ||
let accum = []; | ||
let accumBytes = 0; | ||
let abort = false; | ||
// allow timeout on slow response body | ||
if (_this2.timeout) { | ||
resTimeout = setTimeout(function () { | ||
abort = true; | ||
reject(new FetchError('Response timeout while trying to fetch ' + _this2.url + ' (over ' + _this2.timeout + 'ms)', 'body-timeout')); | ||
}, _this2.timeout); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
let resTimeout; | ||
// handle stream error, such as incorrect content-encoding | ||
_this2.body.on('error', function (err) { | ||
reject(new FetchError('Invalid response body while trying to fetch ' + _this2.url + ': ' + err.message, 'system', err)); | ||
}); | ||
// allow timeout on slow response body | ||
if (_this2.timeout) { | ||
resTimeout = setTimeout(function () { | ||
abort = true; | ||
reject(new FetchError(`Response timeout while trying to fetch ${_this2.url} (over ${_this2.timeout}ms)`, 'body-timeout')); | ||
}, _this2.timeout); | ||
} | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
// handle stream error, such as incorrect content-encoding | ||
_this2.body.on('error', function (err) { | ||
reject(new FetchError(`Invalid response body while trying to fetch ${_this2.url}: ${err.message}`, 'system', err)); | ||
}); | ||
if (_this2.size && accumBytes + chunk.length > _this2.size) { | ||
abort = true; | ||
reject(new FetchError('content size at ' + _this2.url + ' over limit: ' + _this2.size, 'max-size')); | ||
return; | ||
} | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
if (_this2.size && accumBytes + chunk.length > _this2.size) { | ||
abort = true; | ||
reject(new FetchError(`content size at ${_this2.url} over limit: ${_this2.size}`, 'max-size')); | ||
return; | ||
} | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
}; | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
_createClass(Body, [{ | ||
key: 'bodyUsed', | ||
get: function get() { | ||
return this[DISTURBED]; | ||
} | ||
}]); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
} | ||
return Body; | ||
}(); | ||
/** | ||
* Detect buffer encoding and convert to target encoding | ||
* ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding | ||
* | ||
* @param Buffer buffer Incoming buffer | ||
* @param String encoding Target encoding | ||
* @return String | ||
*/ | ||
function convertBody(buffer, headers) { | ||
const ct = headers.get('content-type'); | ||
let charset = 'utf-8'; | ||
let res, str; | ||
var ct = headers.get('content-type'); | ||
var charset = 'utf-8'; | ||
var res = void 0, | ||
str = void 0; | ||
@@ -453,5 +484,6 @@ // header | ||
*/ | ||
function clone(instance) { | ||
let p1, p2; | ||
let body = instance.body; | ||
function clone$1(instance) { | ||
var p1 = void 0, | ||
p2 = void 0; | ||
var body = instance.body; | ||
@@ -465,6 +497,6 @@ // don't allow cloning a used body | ||
// note: we can't clone the form-data object without having it as a dependency | ||
if (body instanceof Stream__default && typeof body.getBoundary !== 'function') { | ||
if (bodyStream(body) && typeof body.getBoundary !== 'function') { | ||
// tee instance body | ||
p1 = new Stream.PassThrough(); | ||
p2 = new Stream.PassThrough(); | ||
p1 = new stream.PassThrough(); | ||
p2 = new stream.PassThrough(); | ||
body.pipe(p1); | ||
@@ -490,3 +522,3 @@ body.pipe(p2); | ||
function extractContentType(instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
@@ -510,3 +542,3 @@ // istanbul ignore if: Currently, because of a guard in Request, body | ||
// detect form data input from form-data module | ||
return `multipart/form-data;boundary=${body.getBoundary()}`; | ||
return 'multipart/form-data;boundary=' + body.getBoundary(); | ||
} else { | ||
@@ -520,5 +552,4 @@ // body is stream | ||
function getTotalBytes(instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
// istanbul ignore if: included for completion | ||
@@ -553,3 +584,3 @@ if (body === null) { | ||
function writeToStream(dest, instance) { | ||
const body = instance.body; | ||
var body = instance.body; | ||
@@ -626,3 +657,3 @@ | ||
if (!isValidTokenChar(val.charCodeAt(0))) return false; | ||
const len = val.length; | ||
var len = val.length; | ||
if (len > 1) { | ||
@@ -680,3 +711,3 @@ if (!isValidTokenChar(val.charCodeAt(1))) return false; | ||
if (!checkIsHttpToken(name)) { | ||
throw new TypeError(`${name} is not a legal HTTP header name`); | ||
throw new TypeError(name + ' is not a legal HTTP header name'); | ||
} | ||
@@ -689,3 +720,3 @@ return name.toLowerCase(); | ||
if (checkInvalidHeaderChar(value)) { | ||
throw new TypeError(`${value} is not a legal HTTP header value`); | ||
throw new TypeError(value + ' is not a legal HTTP header value'); | ||
} | ||
@@ -695,4 +726,5 @@ return value; | ||
const MAP = Symbol('map'); | ||
class Headers { | ||
var MAP = _Symbol('map'); | ||
var Headers = function () { | ||
/** | ||
@@ -704,20 +736,9 @@ * Headers class | ||
*/ | ||
constructor() { | ||
let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
function Headers() { | ||
var init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
this[MAP] = Object.create(null); | ||
_classCallCheck(this, Headers); | ||
if (init instanceof Headers) { | ||
const rawHeaders = init.raw(); | ||
const headerNames = Object.keys(rawHeaders); | ||
this[MAP] = _Object$create(null); | ||
for (const headerName of headerNames) { | ||
for (const value of rawHeaders[headerName]) { | ||
this.append(headerName, value); | ||
} | ||
} | ||
return; | ||
} | ||
// We don't worry about converting prop to ByteString here as append() | ||
@@ -728,3 +749,3 @@ // will handle it. | ||
} else if (typeof init === 'object') { | ||
const method = init[Symbol.iterator]; | ||
var method = init[_Symbol$iterator]; | ||
if (method != null) { | ||
@@ -737,20 +758,59 @@ if (typeof method !== 'function') { | ||
// 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') { | ||
var pairs = []; | ||
for (var _iterator = init, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var pair = _ref; | ||
if (typeof pair !== 'object' || typeof pair[_Symbol$iterator] !== 'function') { | ||
throw new TypeError('Each header pair must be iterable'); | ||
} | ||
pairs.push(Array.from(pair)); | ||
pairs.push(_Array$from(pair)); | ||
} | ||
for (const pair of pairs) { | ||
if (pair.length !== 2) { | ||
for (var _iterator2 = pairs, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) { | ||
var _ref2; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref2 = _i2.value; | ||
} | ||
var _pair = _ref2; | ||
if (_pair.length !== 2) { | ||
throw new TypeError('Each header pair must be a name/value tuple'); | ||
} | ||
this.append(pair[0], pair[1]); | ||
this.append(_pair[0], _pair[1]); | ||
} | ||
} else { | ||
// record<ByteString, ByteString> | ||
for (const key of Object.keys(init)) { | ||
const value = init[key]; | ||
for (var _iterator3 = _Object$keys(init), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _getIterator(_iterator3);;) { | ||
var _ref3; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref3 = _iterator3[_i3++]; | ||
} else { | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref3 = _i3.value; | ||
} | ||
var key = _ref3; | ||
var value = init[key]; | ||
this.append(key, value); | ||
@@ -763,3 +823,3 @@ } | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
value: 'Headers', | ||
@@ -778,4 +838,6 @@ writable: false, | ||
*/ | ||
get(name) { | ||
const list = this[MAP][sanitizeName(name)]; | ||
Headers.prototype.get = function get(name) { | ||
var list = this[MAP][sanitizeName(name)]; | ||
if (!list) { | ||
@@ -785,4 +847,4 @@ return null; | ||
return list.join(', '); | ||
} | ||
return list.join(','); | ||
}; | ||
@@ -796,11 +858,13 @@ /** | ||
*/ | ||
forEach(callback) { | ||
let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
let pairs = getHeaderPairs(this); | ||
let i = 0; | ||
Headers.prototype.forEach = function forEach(callback) { | ||
var thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
var pairs = getHeaderPairs(this); | ||
var i = 0; | ||
while (i < pairs.length) { | ||
var _pairs$i = pairs[i]; | ||
const name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
var _pairs$i = pairs[i], | ||
name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
@@ -811,3 +875,3 @@ callback.call(thisArg, value, name, this); | ||
} | ||
} | ||
}; | ||
@@ -821,5 +885,7 @@ /** | ||
*/ | ||
set(name, value) { | ||
Headers.prototype.set = function set(name, value) { | ||
this[MAP][sanitizeName(name)] = [sanitizeValue(value)]; | ||
} | ||
}; | ||
@@ -833,3 +899,5 @@ /** | ||
*/ | ||
append(name, value) { | ||
Headers.prototype.append = function append(name, value) { | ||
if (!this.has(name)) { | ||
@@ -841,3 +909,3 @@ this.set(name, value); | ||
this[MAP][sanitizeName(name)].push(sanitizeValue(value)); | ||
} | ||
}; | ||
@@ -850,5 +918,7 @@ /** | ||
*/ | ||
has(name) { | ||
Headers.prototype.has = function has(name) { | ||
return !!this[MAP][sanitizeName(name)]; | ||
} | ||
}; | ||
@@ -861,5 +931,7 @@ /** | ||
*/ | ||
delete(name) { | ||
Headers.prototype.delete = function _delete(name) { | ||
delete this[MAP][sanitizeName(name)]; | ||
} | ||
}; | ||
@@ -871,5 +943,5 @@ /** | ||
*/ | ||
raw() { | ||
Headers.prototype.raw = function raw() { | ||
return this[MAP]; | ||
} | ||
}; | ||
@@ -881,5 +953,7 @@ /** | ||
*/ | ||
keys() { | ||
Headers.prototype.keys = function keys() { | ||
return createHeadersIterator(this, 'key'); | ||
} | ||
}; | ||
@@ -891,5 +965,7 @@ /** | ||
*/ | ||
values() { | ||
Headers.prototype.values = function values() { | ||
return createHeadersIterator(this, 'value'); | ||
} | ||
}; | ||
@@ -903,9 +979,14 @@ /** | ||
*/ | ||
[Symbol.iterator]() { | ||
Headers.prototype[_Symbol$iterator] = function () { | ||
return createHeadersIterator(this, 'key+value'); | ||
} | ||
} | ||
Headers.prototype.entries = Headers.prototype[Symbol.iterator]; | ||
}; | ||
Object.defineProperty(Headers.prototype, Symbol.toStringTag, { | ||
return Headers; | ||
}(); | ||
Headers.prototype.entries = Headers.prototype[_Symbol$iterator]; | ||
_Object$defineProperty(Headers.prototype, _Symbol$toStringTag, { | ||
value: 'HeadersPrototype', | ||
@@ -918,3 +999,3 @@ writable: false, | ||
function getHeaderPairs(headers, kind) { | ||
const keys = Object.keys(headers[MAP]).sort(); | ||
var keys = _Object$keys(headers[MAP]).sort(); | ||
return keys.map(kind === 'key' ? function (k) { | ||
@@ -927,9 +1008,9 @@ return [k]; | ||
const INTERNAL = Symbol('internal'); | ||
var INTERNAL = _Symbol('internal'); | ||
function createHeadersIterator(target, kind) { | ||
const iterator = Object.create(HeadersIteratorPrototype); | ||
var iterator = _Object$create(HeadersIteratorPrototype); | ||
iterator[INTERNAL] = { | ||
target, | ||
kind, | ||
target: target, | ||
kind: kind, | ||
index: 0 | ||
@@ -940,16 +1021,16 @@ }; | ||
const HeadersIteratorPrototype = Object.setPrototypeOf({ | ||
next() { | ||
var HeadersIteratorPrototype = _Object$setPrototypeOf({ | ||
next: function next() { | ||
// istanbul ignore if | ||
if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { | ||
if (!this || _Object$getPrototypeOf(this) !== HeadersIteratorPrototype) { | ||
throw new TypeError('Value of `this` is not a HeadersIterator'); | ||
} | ||
var _INTERNAL = this[INTERNAL]; | ||
const target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var _INTERNAL = this[INTERNAL], | ||
target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
const values = getHeaderPairs(target, kind); | ||
const len = values.length; | ||
var values = getHeaderPairs(target, kind); | ||
var len = values.length; | ||
if (index >= len) { | ||
@@ -962,6 +1043,6 @@ return { | ||
const pair = values[index]; | ||
var pair = values[index]; | ||
this[INTERNAL].index = index + 1; | ||
let result; | ||
var result = void 0; | ||
if (kind === 'key') { | ||
@@ -980,5 +1061,12 @@ result = pair[0]; | ||
} | ||
}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); | ||
}, _Object$getPrototypeOf(_Object$getPrototypeOf(_getIterator([])))); | ||
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { | ||
// On Node.js v0.12 the %IteratorPrototype% object is broken | ||
if (typeof HeadersIteratorPrototype[_Symbol$iterator] !== 'function') { | ||
HeadersIteratorPrototype[_Symbol$iterator] = function () { | ||
return this; | ||
}; | ||
} | ||
_Object$defineProperty(HeadersIteratorPrototype, _Symbol$toStringTag, { | ||
value: 'HeadersIterator', | ||
@@ -1003,16 +1091,20 @@ writable: false, | ||
*/ | ||
class Response { | ||
constructor() { | ||
let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
Body.call(this, body, opts); | ||
var Response = function (_Body) { | ||
_inherits(Response, _Body); | ||
this.url = opts.url; | ||
this.status = opts.status || 200; | ||
this.statusText = opts.statusText || http.STATUS_CODES[this.status]; | ||
function Response() { | ||
var body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.headers = new Headers(opts.headers); | ||
_classCallCheck(this, Response); | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, body, opts)); | ||
_this.url = opts.url; | ||
_this.status = opts.status || 200; | ||
_this.statusText = opts.statusText || http.STATUS_CODES[_this.status]; | ||
_this.headers = new Headers(opts.headers); | ||
_Object$defineProperty(_this, _Symbol$toStringTag, { | ||
value: 'Response', | ||
@@ -1023,2 +1115,3 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
@@ -1029,6 +1122,4 @@ | ||
*/ | ||
get ok() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
/** | ||
@@ -1039,5 +1130,5 @@ * Clone this response | ||
*/ | ||
clone() { | ||
Response.prototype.clone = function clone() { | ||
return new Response(clone(this), { | ||
return new Response(clone$1(this), { | ||
url: this.url, | ||
@@ -1049,8 +1140,15 @@ status: this.status, | ||
}); | ||
} | ||
} | ||
}; | ||
Body.mixIn(Response.prototype); | ||
_createClass(Response, [{ | ||
key: 'ok', | ||
get: function get() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
}]); | ||
Object.defineProperty(Response.prototype, Symbol.toStringTag, { | ||
return Response; | ||
}(Body); | ||
_Object$defineProperty(Response.prototype, _Symbol$toStringTag, { | ||
value: 'ResponsePrototype', | ||
@@ -1068,3 +1166,3 @@ writable: false, | ||
const PARSED_URL = Symbol('url'); | ||
var PARSED_URL = _Symbol('url'); | ||
@@ -1078,8 +1176,13 @@ /** | ||
*/ | ||
class Request { | ||
constructor(input) { | ||
let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
let parsedURL; | ||
var Request = function (_Body) { | ||
_inherits(Request, _Body); | ||
function Request(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, Request); | ||
var parsedURL = void 0; | ||
// normalize input | ||
@@ -1094,3 +1197,3 @@ if (!(input instanceof Request)) { | ||
// coerce input to a string before attempting to parse | ||
parsedURL = url.parse(`${input}`); | ||
parsedURL = url.parse('' + input); | ||
} | ||
@@ -1102,3 +1205,3 @@ input = {}; | ||
let method = init.method || input.method || 'GET'; | ||
var method = init.method || input.method || 'GET'; | ||
@@ -1109,18 +1212,18 @@ if ((init.body != null || input instanceof Request && input.body !== null) && (method === 'GET' || method === 'HEAD')) { | ||
let inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone(input) : null; | ||
var inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone$1(input) : null; | ||
Body.call(this, inputBody, { | ||
// fetch spec options | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, inputBody, { | ||
timeout: init.timeout || input.timeout || 0, | ||
size: init.size || input.size || 0 | ||
}); | ||
})); | ||
// fetch spec options | ||
this.method = method.toUpperCase(); | ||
this.redirect = init.redirect || input.redirect || 'follow'; | ||
this.headers = new Headers(init.headers || input.headers || {}); | ||
_this.method = method.toUpperCase(); | ||
_this.redirect = init.redirect || input.redirect || 'follow'; | ||
_this.headers = new Headers(init.headers || input.headers || {}); | ||
if (init.body != null) { | ||
const contentType = extractContentType(this); | ||
if (contentType !== null && !this.headers.has('Content-Type')) { | ||
this.headers.append('Content-Type', contentType); | ||
var contentType = extractContentType(_this); | ||
if (contentType !== null && !_this.headers.has('Content-Type')) { | ||
_this.headers.append('Content-Type', contentType); | ||
} | ||
@@ -1130,9 +1233,9 @@ } | ||
// server 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.counter = init.counter || input.counter || 0; | ||
this.agent = init.agent || input.agent; | ||
_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.counter = init.counter || input.counter || 0; | ||
_this.agent = init.agent || input.agent; | ||
this[PARSED_URL] = parsedURL; | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
_this[PARSED_URL] = parsedURL; | ||
_Object$defineProperty(_this, _Symbol$toStringTag, { | ||
value: 'Request', | ||
@@ -1143,8 +1246,5 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
get url() { | ||
return url.format(this[PARSED_URL]); | ||
} | ||
/** | ||
@@ -1155,10 +1255,17 @@ * Clone this request | ||
*/ | ||
clone() { | ||
Request.prototype.clone = function clone$1() { | ||
return new Request(this); | ||
} | ||
} | ||
}; | ||
Body.mixIn(Request.prototype); | ||
_createClass(Request, [{ | ||
key: 'url', | ||
get: function get() { | ||
return url.format(this[PARSED_URL]); | ||
} | ||
}]); | ||
Object.defineProperty(Request.prototype, Symbol.toStringTag, { | ||
return Request; | ||
}(Body); | ||
_Object$defineProperty(Request.prototype, _Symbol$toStringTag, { | ||
value: 'RequestPrototype', | ||
@@ -1171,4 +1278,3 @@ writable: false, | ||
function getNodeRequestOptions(request) { | ||
const parsedURL = request[PARSED_URL]; | ||
const headers = new Headers(request.headers); | ||
var headers = new Headers(request.headers); | ||
@@ -1181,12 +1287,8 @@ // fetch step 3 | ||
// Basic fetch | ||
if (!parsedURL.protocol || !parsedURL.hostname) { | ||
throw new TypeError('Only absolute URLs are supported'); | ||
if (!/^https?:$/.test(request[PARSED_URL].protocol)) { | ||
throw new Error('only http(s) protocols are supported'); | ||
} | ||
if (!/^https?:$/.test(parsedURL.protocol)) { | ||
throw new TypeError('Only HTTP(S) protocols are supported'); | ||
} | ||
// HTTP-network-or-cache fetch steps 5-9 | ||
let contentLengthValue = null; | ||
var contentLengthValue = null; | ||
if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { | ||
@@ -1196,3 +1298,3 @@ contentLengthValue = '0'; | ||
if (request.body != null) { | ||
const totalBytes = getTotalBytes(request); | ||
var totalBytes = getTotalBytes(request); | ||
if (typeof totalBytes === 'number') { | ||
@@ -1222,3 +1324,3 @@ contentLengthValue = String(totalBytes); | ||
return Object.assign({}, parsedURL, { | ||
return _Object$assign({}, request[PARSED_URL], { | ||
method: request.method, | ||
@@ -1255,7 +1357,12 @@ headers: headers.raw(), | ||
// build request object | ||
const request = new Request(url$$1, opts); | ||
const options = getNodeRequestOptions(request); | ||
var request = new Request(url$$1, opts); | ||
const send = (options.protocol === 'https:' ? https : http).request; | ||
var options = getNodeRequestOptions(request); | ||
if (!options.protocol || !options.hostname) { | ||
throw new Error('only absolute urls are supported'); | ||
} | ||
var send = (options.protocol === 'https:' ? https : http).request; | ||
// http.request only support string as host header, this hack make custom host header possible | ||
@@ -1267,4 +1374,4 @@ if (options.headers.host) { | ||
// send request | ||
const req = send(options); | ||
let reqTimeout; | ||
var req = send(options); | ||
var reqTimeout = void 0; | ||
@@ -1275,3 +1382,3 @@ if (request.timeout) { | ||
req.abort(); | ||
reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); | ||
reject(new FetchError('network timeout at: ' + request.url, 'request-timeout')); | ||
}, request.timeout); | ||
@@ -1283,3 +1390,3 @@ }); | ||
clearTimeout(reqTimeout); | ||
reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); | ||
reject(new FetchError('request to ' + request.url + ' failed, reason: ' + err.message, 'system', err)); | ||
}); | ||
@@ -1293,3 +1400,3 @@ | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
reject(new FetchError('redirect mode is set to error: ' + request.url, 'no-redirect')); | ||
return; | ||
@@ -1299,3 +1406,3 @@ } | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
reject(new FetchError('maximum redirect reached at: ' + request.url, 'max-redirect')); | ||
return; | ||
@@ -1305,3 +1412,3 @@ } | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
reject(new FetchError('redirect location header missing at: ' + request.url, 'invalid-redirect')); | ||
return; | ||
@@ -1324,10 +1431,36 @@ } | ||
// normalize location header for manual redirect mode | ||
const headers = new Headers(); | ||
for (const name of Object.keys(res.headers)) { | ||
if (Array.isArray(res.headers[name])) { | ||
for (const val of res.headers[name]) { | ||
headers.append(name, val); | ||
var headers = new Headers(); | ||
for (var _iterator = _Object$keys(res.headers), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _name = _ref; | ||
if (Array.isArray(res.headers[_name])) { | ||
for (var _iterator2 = res.headers[_name], _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) { | ||
var _ref2; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref2 = _i2.value; | ||
} | ||
var val = _ref2; | ||
headers.append(_name, val); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
headers.append(_name, res.headers[_name]); | ||
} | ||
@@ -1340,4 +1473,4 @@ } | ||
// prepare response | ||
let body = res.pipe(new Stream.PassThrough()); | ||
const response_options = { | ||
var body = res.pipe(new stream.PassThrough()); | ||
var response_options = { | ||
url: request.url, | ||
@@ -1351,47 +1484,41 @@ status: res.statusCode, | ||
// HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); | ||
// response object | ||
var output = void 0; | ||
// HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. compression support is disabled | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 3. no content-encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { | ||
resolve$$1(new Response(body, response_options)); | ||
if (!request.compress || request.method === 'HEAD' || !headers.has('content-encoding') || res.statusCode === 204 || res.statusCode === 304) { | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
} | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
const zlibOptions = { | ||
flush: zlib.Z_SYNC_FLUSH, | ||
finishFlush: zlib.Z_SYNC_FLUSH | ||
}; | ||
// otherwise, check for gzip or deflate | ||
var name = headers.get('content-encoding'); | ||
// for gzip | ||
if (codings == 'gzip' || codings == 'x-gzip') { | ||
body = body.pipe(zlib.createGunzip(zlibOptions)); | ||
resolve$$1(new Response(body, response_options)); | ||
if (name == 'gzip' || name == 'x-gzip') { | ||
body = body.pipe(zlib.createGunzip()); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
} | ||
// for deflate | ||
if (codings == 'deflate' || codings == 'x-deflate') { | ||
// for deflate | ||
} else if (name == 'deflate' || name == 'x-deflate') { | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new Stream.PassThrough()); | ||
var raw = res.pipe(new stream.PassThrough()); | ||
raw.once('data', function (chunk) { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(zlib.createInflate(zlibOptions)); | ||
body = body.pipe(zlib.createInflate()); | ||
} else { | ||
body = body.pipe(zlib.createInflateRaw(zlibOptions)); | ||
body = body.pipe(zlib.createInflateRaw()); | ||
} | ||
resolve$$1(new Response(body, response_options)); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
}); | ||
@@ -1402,3 +1529,5 @@ return; | ||
// otherwise, use response as-is | ||
resolve$$1(new Response(body, response_options)); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
}); | ||
@@ -1427,2 +1556,1 @@ | ||
exports.Response = Response; | ||
exports.FetchError = FetchError; |
{ | ||
"_args": [ | ||
[ | ||
{ | ||
"raw": "./node-fetch-pkg.tgz", | ||
"scope": null, | ||
"escapedName": null, | ||
"name": null, | ||
"rawSpec": "./node-fetch-pkg.tgz", | ||
"spec": "/Users/zkat/Documents/code/make-fetch-happen/node-fetch-pkg.tgz", | ||
"type": "local" | ||
}, | ||
"/Users/zkat/Documents/code/make-fetch-happen" | ||
] | ||
], | ||
"_from": "node-fetch-pkg.tgz", | ||
"_id": "node-fetch@2.0.0-alpha.3", | ||
"_inCache": true, | ||
"_integrity": "sha1-gDG97WccgG5GBMFrJ6sZc7T+iMw=", | ||
"_location": "/node-fetch", | ||
"_phantomChildren": {}, | ||
"_requested": { | ||
"raw": "./node-fetch-pkg.tgz", | ||
"scope": null, | ||
"escapedName": null, | ||
"name": null, | ||
"rawSpec": "./node-fetch-pkg.tgz", | ||
"spec": "/Users/zkat/Documents/code/make-fetch-happen/node-fetch-pkg.tgz", | ||
"type": "local" | ||
"type": "version", | ||
"registry": true, | ||
"raw": "node-fetch@2.0.0-alpha.3", | ||
"name": "node-fetch", | ||
"escapedName": "node-fetch", | ||
"rawSpec": "2.0.0-alpha.3", | ||
"saveSpec": null, | ||
"fetchSpec": "2.0.0-alpha.3" | ||
}, | ||
"_requiredBy": [ | ||
"#USER", | ||
"/" | ||
], | ||
"_resolved": "file:node-fetch-pkg.tgz", | ||
"_shasum": "f69cc2e2f42d94d5e1fa62a43c33238853016cb8", | ||
"_resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.0.0-alpha.3.tgz", | ||
"_shrinkwrap": null, | ||
"_spec": "./node-fetch-pkg.tgz", | ||
"_spec": "node-fetch@2.0.0-alpha.3", | ||
"_where": "/Users/zkat/Documents/code/make-fetch-happen", | ||
@@ -42,13 +26,21 @@ "author": { | ||
}, | ||
"bin": null, | ||
"bugs": { | ||
"url": "https://github.com/bitinn/node-fetch/issues" | ||
}, | ||
"bundleDependencies": false, | ||
"dependencies": { | ||
"encoding": "^0.1.11" | ||
"babel-runtime": "^6.11.6", | ||
"buffer-to-arraybuffer": "0.0.4", | ||
"encoding": "^0.1.11", | ||
"is-stream": "^1.0.1" | ||
}, | ||
"deprecated": false, | ||
"description": "A light-weight module that brings window.fetch to node.js", | ||
"devDependencies": { | ||
"babel-plugin-istanbul": "^4.0.0", | ||
"babel-preset-env": "^1.1.10", | ||
"babel-plugin-istanbul": "^3.0.0", | ||
"babel-plugin-transform-runtime": "^6.15.0", | ||
"babel-preset-env": "^1.1.8", | ||
"babel-register": "^6.16.3", | ||
"bluebird": "^3.3.4", | ||
"chai": "^3.5.0", | ||
@@ -59,3 +51,3 @@ "chai-as-promised": "^6.0.0", | ||
"codecov": "^1.0.1", | ||
"cross-env": "^3.1.4", | ||
"cross-env": "2.0.1", | ||
"form-data": ">=1.0.0", | ||
@@ -68,9 +60,7 @@ "is-builtin-module": "^1.0.0", | ||
"resumer": "0.0.0", | ||
"rollup": "^0.41.4", | ||
"rollup": "^0.37.0", | ||
"rollup-plugin-babel": "^2.6.1", | ||
"rollup-plugin-node-resolve": "^2.0.0", | ||
"whatwg-url": "^4.0.0" | ||
}, | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"files": [ | ||
@@ -91,3 +81,4 @@ "lib/index.js", | ||
"optionalDependencies": {}, | ||
"readme": "\nnode-fetch\n==========\n\n[![npm version][npm-image]][npm-url]\n[![build status][travis-image]][travis-url]\n[![coverage status][codecov-image]][codecov-url]\n\nA light-weight module that brings `window.fetch` to Node.js\n\n\n## Motivation\n\nInstead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `fetch` API directly? Hence `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime.\n\nSee Matt Andrews' [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side).\n\n\n## Features\n\n- Stay consistent with `window.fetch` API.\n- Make conscious trade-off when following [whatwg fetch spec][whatwg-fetch] and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known difference.\n- Use native promise, but allow substituting it with [insert your favorite promise library].\n- Use native stream for body, on both request and response.\n- Decode content encoding (gzip/deflate) properly, and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.\n- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors][] for troubleshooting.\n\n\n## Difference from client-side fetch\n\n- See [Known Differences](https://github.com/bitinn/node-fetch/blob/master/LIMITS.md) for details.\n- If you happen to use a missing feature that `window.fetch` offers, feel free to open an issue.\n- Pull requests are welcomed too!\n\n\n## Install\n\n```sh\n$ npm install node-fetch --save\n```\n\n\n## Usage\n\n```javascript\nimport fetch from 'node-fetch';\n// or\n// const fetch = require('node-fetch');\n\n// if you are using your own Promise library, set it through fetch.Promise. Eg.\n\n// import Bluebird from 'bluebird';\n// fetch.Promise = Bluebird;\n\n// plain text or html\n\nfetch('https://github.com/')\n\t.then(res => res.text())\n\t.then(body => console.log(body));\n\n// json\n\nfetch('https://api.github.com/users/github')\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// catching network error\n// 3xx-5xx responses are NOT network errors, and should be handled in then()\n// you only need one catch() at the end of your promise chain\n\nfetch('http://domain.invalid/')\n\t.catch(err => console.error(err));\n\n// stream\n// the node.js way is to use stream when possible\n\nfetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')\n\t.then(res => {\n\t\tconst dest = fs.createWriteStream('./octocat.png');\n\t\tres.body.pipe(dest);\n\t});\n\n// buffer\n// if you prefer to cache binary data in full, use buffer()\n// note that buffer() is a node-fetch only API\n\nimport fileType from 'file-type';\n\nfetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')\n\t.then(res => res.buffer())\n\t.then(buffer => fileType(buffer))\n\t.then(type => { /* ... */ });\n\n// meta\n\nfetch('https://github.com/')\n\t.then(res => {\n\t\tconsole.log(res.ok);\n\t\tconsole.log(res.status);\n\t\tconsole.log(res.statusText);\n\t\tconsole.log(res.headers.raw());\n\t\tconsole.log(res.headers.get('content-type'));\n\t});\n\n// post\n\nfetch('http://httpbin.org/post', { method: 'POST', body: 'a=1' })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with stream from file\n\nimport { createReadStream } from 'fs';\n\nconst stream = createReadStream('input.txt');\nfetch('http://httpbin.org/post', { method: 'POST', body: stream })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with JSON\n\nvar body = { a: 1 };\nfetch('http://httpbin.org/post', { \n\tmethod: 'POST',\n\tbody: JSON.stringify(body),\n\theaders: { 'Content-Type': 'application/json' },\n})\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with form-data (detect multipart)\n\nimport FormData from 'form-data';\n\nconst form = new FormData();\nform.append('a', 1);\nfetch('http://httpbin.org/post', { method: 'POST', body: form })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with form-data (custom headers)\n// note that getHeaders() is non-standard API\n\nimport FormData from 'form-data';\n\nconst form = new FormData();\nform.append('a', 1);\nfetch('http://httpbin.org/post', { method: 'POST', body: form, headers: form.getHeaders() })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// node 7+ with async function\n\n(async function () {\n\tconst res = await fetch('https://api.github.com/users/github');\n\tconst json = await res.json();\n\tconsole.log(json);\n})();\n```\n\nSee [test cases](https://github.com/bitinn/node-fetch/blob/master/test/test.js) for more examples.\n\n\n## API\n\n### fetch(url[, options])\n\n- `url` A string representing the URL for fetching\n- `options` [Options](#fetch-options) for the HTTP(S) request\n- Returns: <code>Promise<[Response](#class-response)></code>\n\nPerform an HTTP(S) fetch.\n\n`url` should be an absolute url, such as `http://example.com/`. A path-relative URL (`/file/under/root`) or protocol-relative URL (`//can-be-http-or-https.com/`) will result in a rejected promise.\n\n<a id=\"fetch-options\"></a>\n#### Options\n\nThe default values are shown after each option key.\n\n```js\n{\n\t// These properties are part of the Fetch Standard\n\tmethod: 'GET',\n\theaders: {}, // request headers. format is the identical to that accepted by the Headers constructor (see below)\n\tbody: null, // request body. can be null, a string, a Buffer, a Blob, or a Node.js Readable stream\n\tredirect: 'follow', // set to `manual` to extract redirect headers, `error` to reject redirect\n\n\t// The following properties are node-fetch extensions\n\tfollow: 20, // maximum redirect count. 0 to not follow redirect\n\ttimeout: 0, // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies)\n\tcompress: true, // support gzip/deflate content encoding. false to disable\n\tsize: 0, // maximum response body size in bytes. 0 to disable\n\tagent: null // http(s).Agent instance, allows custom proxy, certificate etc.\n}\n```\n\n##### Default Headers\n\nIf no values are set, the following request headers will be sent automatically:\n\nHeader | Value\n----------------- | --------------------------------------------------------\n`Accept-Encoding` | `gzip,deflate` _(when `options.compress === true`)_\n`Accept` | `*/*`\n`Connection` | `close` _(when no `options.agent` is present)_\n`Content-Length` | _(automatically calculated, if possible)_\n`User-Agent` | `node-fetch/1.0 (+https://github.com/bitinn/node-fetch)`\n\n<a id=\"class-request\"></a>\n### Class: Request\n\nAn HTTP(S) request containing information about URL, method, headers, and the body. This class implements the [Body](#iface-body) interface.\n\nDue to the nature of Node.js, the following properties are not implemented at this moment:\n\n- `type`\n- `destination`\n- `referrer`\n- `referrerPolicy`\n- `mode`\n- `credentials`\n- `cache`\n- `integrity`\n- `keepalive`\n\nThe following node-fetch extension properties are provided:\n\n- `follow`\n- `compress`\n- `counter`\n- `agent`\n\nSee [options](#fetch-options) for exact meaning of these extensions.\n\n#### new Request(input[, options])\n\n<small>*(spec-compliant)*</small>\n\n- `input` A string representing a URL, or another `Request` (which will be cloned)\n- `options` [Options][#fetch-options] for the HTTP(S) request\n\nConstructs a new `Request` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request).\n\nIn most cases, directly `fetch(url, options)` is simpler than creating a `Request` object.\n\n<a id=\"class-response\"></a>\n### Class: Response\n\nAn HTTP(S) response. This class implements the [Body](#iface-body) interface.\n\nThe following properties are not implemented in node-fetch at this moment:\n\n- `Response.error()`\n- `Response.redirect()`\n- `type`\n- `redirected`\n- `trailer`\n\n#### new Response([body[, options]])\n\n<small>*(spec-compliant)*</small>\n\n- `body` A string or [Readable stream][node-readable]\n- `options` A [`ResponseInit`][response-init] options dictionary\n\nConstructs a new `Response` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response).\n\nBecause Node.js does not implement service workers (for which this class was designed), one rarely has to construct a `Response` directly.\n\n<a id=\"class-headers\"></a>\n### Class: Headers\n\nThis class allows manipulating and iterating over a set of HTTP headers. All methods specified in the [Fetch Standard][whatwg-fetch] are implemented.\n\n#### new Headers([init])\n\n<small>*(spec-compliant)*</small>\n\n- `init` Optional argument to pre-fill the `Headers` object\n\nConstruct a new `Headers` object. `init` can be either `null`, a `Headers` object, an key-value map object, or any iterable object.\n\n```js\n// Example adapted from https://fetch.spec.whatwg.org/#example-headers-class\n\nconst meta = {\n 'Content-Type': 'text/xml',\n 'Breaking-Bad': '<3'\n};\nconst headers = new Headers(meta);\n\n// The above is equivalent to\nconst meta = [\n [ 'Content-Type', 'text/xml' ],\n [ 'Breaking-Bad', '<3' ]\n];\nconst headers = new Headers(meta);\n\n// You can in fact use any iterable objects, like a Map or even another Headers\nconst meta = new Map();\nmeta.set('Content-Type', 'text/xml');\nmeta.set('Breaking-Bad', '<3');\nconst headers = new Headers(meta);\nconst copyOfHeaders = new Headers(headers);\n```\n\n<a id=\"iface-body\"></a>\n### Interface: Body\n\n`Body` is an abstract interface with methods that are applicable to both `Request` and `Response` classes.\n\nThe following methods are not yet implemented in node-fetch at this moment:\n\n- `formData()`\n\n#### body.body\n\n<small>*(deviation from spec)*</small>\n\n* Node.js [`Readable` stream][node-readable]\n\nThe data encapsulated in the `Body` object. Note that while the [Fetch Standard][whatwg-fetch] requires the property to always be a WHATWG `ReadableStream`, in node-fetch it is a Node.js [`Readable` stream][node-readable].\n\n#### body.bodyUsed\n\n<small>*(spec-compliant)*</small>\n\n* `Boolean`\n\nA boolean property for if this body has been consumed. Per spec, a consumed body cannot be used again.\n\n#### body.arrayBuffer()\n#### body.blob()\n#### body.json()\n#### body.text()\n\n<small>*(spec-compliant)*</small>\n\n* Returns: <code>Promise</code>\n\nConsume the body and return a promise that will resolve to one of these formats.\n\n#### body.buffer()\n\n<small>*(node-fetch extension)*</small>\n\n* Returns: <code>Promise<Buffer></code>\n\nConsume the body and return a promise that will resolve to a Buffer.\n\n#### body.textConverted()\n\n<small>*(node-fetch extension)*</small>\n\n* Returns: <code>Promise<String></code>\n\nIdentical to `body.text()`, except instead of always converting to UTF-8, encoding sniffing will be performed and text converted to UTF-8, if possible.\n\n<a id=\"class-fetcherror\"></a>\n### Class: FetchError\n\n<small>*(node-fetch extension)*</small>\n\nAn operational error in the fetching process. See [ERROR-HANDLING.md][] for more info.\n\n## License\n\nMIT\n\n\n## Acknowledgement\n\nThanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.\n\n\n[npm-image]: https://img.shields.io/npm/v/node-fetch.svg?style=flat-square\n[npm-url]: https://www.npmjs.com/package/node-fetch\n[travis-image]: https://img.shields.io/travis/bitinn/node-fetch.svg?style=flat-square\n[travis-url]: https://travis-ci.org/bitinn/node-fetch\n[codecov-image]: https://img.shields.io/codecov/c/github/bitinn/node-fetch.svg?style=flat-square\n[codecov-url]: https://codecov.io/gh/bitinn/node-fetch\n[ERROR-HANDLING.md]: https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md\n[whatwg-fetch]: https://fetch.spec.whatwg.org/\n[response-init]: https://fetch.spec.whatwg.org/#responseinit\n[node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams\n[mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers\n", | ||
"peerDependencies": {}, | ||
"readme": "\nnode-fetch\n==========\n\n[![npm version][npm-image]][npm-url]\n[![build status][travis-image]][travis-url]\n[![coverage status][codecov-image]][codecov-url]\n\nA light-weight module that brings `window.fetch` to Node.js\n\n\n# Motivation\n\nInstead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `Fetch` API directly? Hence `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime.\n\nSee Matt Andrews' [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side).\n\n\n# Features\n\n- Stay consistent with `window.fetch` API.\n- Make conscious trade-off when following [whatwg fetch spec](https://fetch.spec.whatwg.org/) and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known difference.\n- Use native promise, but allow substituting it with [insert your favorite promise library].\n- Use native stream for body, on both request and response.\n- Decode content encoding (gzip/deflate) properly, and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.\n- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors](https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md) for troubleshooting.\n\n\n# Difference from client-side fetch\n\n- See [Known Differences](https://github.com/bitinn/node-fetch/blob/master/LIMITS.md) for details.\n- If you happen to use a missing feature that `window.fetch` offers, feel free to open an issue.\n- Pull requests are welcomed too!\n\n\n# Install\n\n`npm install node-fetch --save`\n\n\n# Usage\n\n```javascript\nimport fetch from 'node-fetch';\n// or\n// const fetch = require('node-fetch');\n\n// if you are using your own Promise library, set it through fetch.Promise. Eg.\n\n// import Bluebird from 'bluebird';\n// fetch.Promise = Bluebird;\n\n// plain text or html\n\nfetch('https://github.com/')\n\t.then(res => res.text())\n\t.then(body => console.log(body));\n\n// json\n\nfetch('https://api.github.com/users/github')\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// catching network error\n// 3xx-5xx responses are NOT network errors, and should be handled in then()\n// you only need one catch() at the end of your promise chain\n\nfetch('http://domain.invalid/')\n\t.catch(err => console.error(err));\n\n// stream\n// the node.js way is to use stream when possible\n\nfetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')\n\t.then(res => {\n\t\tconst dest = fs.createWriteStream('./octocat.png');\n\t\tres.body.pipe(dest);\n\t});\n\n// buffer\n// if you prefer to cache binary data in full, use buffer()\n// note that buffer() is a node-fetch only API\n\nimport fileType from 'file-type';\n\nfetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')\n\t.then(res => res.buffer())\n\t.then(buffer => fileType(buffer))\n\t.then(type => { /* ... */ });\n\n// meta\n\nfetch('https://github.com/')\n\t.then(res => {\n\t\tconsole.log(res.ok);\n\t\tconsole.log(res.status);\n\t\tconsole.log(res.statusText);\n\t\tconsole.log(res.headers.raw());\n\t\tconsole.log(res.headers.get('content-type'));\n\t});\n\n// post\n\nfetch('http://httpbin.org/post', { method: 'POST', body: 'a=1' })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with stream from file\n\nimport { createReadStream } from 'fs';\n\nconst stream = createReadStream('input.txt');\nfetch('http://httpbin.org/post', { method: 'POST', body: stream })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with JSON\n\nvar body = { a: 1 };\nfetch('http://httpbin.org/post', { \n\tmethod: 'POST',\n\tbody: JSON.stringify(body),\n\theaders: { 'Content-Type': 'application/json' },\n})\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with form-data (detect multipart)\n\nimport FormData from 'form-data';\n\nconst form = new FormData();\nform.append('a', 1);\nfetch('http://httpbin.org/post', { method: 'POST', body: form })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// post with form-data (custom headers)\n// note that getHeaders() is non-standard API\n\nimport FormData from 'form-data';\n\nconst form = new FormData();\nform.append('a', 1);\nfetch('http://httpbin.org/post', { method: 'POST', body: form, headers: form.getHeaders() })\n\t.then(res => res.json())\n\t.then(json => console.log(json));\n\n// node 7+ with async function\n\n(async function () {\n\tconst res = await fetch('https://api.github.com/users/github');\n\tconst json = await res.json();\n\tconsole.log(json);\n})();\n```\n\nSee [test cases](https://github.com/bitinn/node-fetch/blob/master/test/test.js) for more examples.\n\n\n# API\n\n## fetch(url, options)\n\nReturns a `Promise`\n\n### Url\n\nShould be an absolute url, eg `http://example.com/`\n\n### Options\n\nNote that only `method`, `headers`, `redirect` and `body` are allowed in `window.fetch`. Other options are node.js extensions. The default values are shown after each option key.\n\n```\n{\n\tmethod: 'GET'\n\t, headers: {} // request header. format {a:'1'} or {b:['1','2','3']}\n\t, redirect: 'follow' // set to `manual` to extract redirect headers, `error` to reject redirect\n\t, follow: 20 // maximum redirect count. 0 to not follow redirect\n\t, timeout: 0 // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies)\n\t, compress: true // support gzip/deflate content encoding. false to disable\n\t, size: 0 // maximum response body size in bytes. 0 to disable\n\t, body: empty // request body. can be a string, buffer, readable stream\n\t, agent: null // http.Agent instance, allows custom proxy, certificate etc.\n}\n```\n\n\n# License\n\nMIT\n\n\n# Acknowledgement\n\nThanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.\n\n\n[npm-image]: https://img.shields.io/npm/v/node-fetch.svg?style=flat-square\n[npm-url]: https://www.npmjs.com/package/node-fetch\n[travis-image]: https://img.shields.io/travis/bitinn/node-fetch.svg?style=flat-square\n[travis-url]: https://travis-ci.org/bitinn/node-fetch\n[codecov-image]: https://img.shields.io/codecov/c/github/bitinn/node-fetch.svg?style=flat-square\n[codecov-url]: https://codecov.io/gh/bitinn/node-fetch\n", | ||
"readmeFilename": "README.md", | ||
@@ -94,0 +85,0 @@ "repository": { |
@@ -12,5 +12,5 @@ | ||
## Motivation | ||
# Motivation | ||
Instead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `fetch` API directly? Hence `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime. | ||
Instead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `Fetch` API directly? Hence `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime. | ||
@@ -20,13 +20,13 @@ See Matt Andrews' [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side). | ||
## Features | ||
# Features | ||
- Stay consistent with `window.fetch` API. | ||
- Make conscious trade-off when following [whatwg fetch spec][whatwg-fetch] and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known difference. | ||
- Make conscious trade-off when following [whatwg fetch spec](https://fetch.spec.whatwg.org/) and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known difference. | ||
- Use native promise, but allow substituting it with [insert your favorite promise library]. | ||
- Use native stream for body, on both request and response. | ||
- 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][] for troubleshooting. | ||
- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors](https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md) for troubleshooting. | ||
## Difference from client-side fetch | ||
# Difference from client-side fetch | ||
@@ -38,10 +38,8 @@ - See [Known Differences](https://github.com/bitinn/node-fetch/blob/master/LIMITS.md) for details. | ||
## Install | ||
# Install | ||
```sh | ||
$ npm install node-fetch --save | ||
``` | ||
`npm install node-fetch --save` | ||
## Usage | ||
# Usage | ||
@@ -167,211 +165,37 @@ ```javascript | ||
## API | ||
# API | ||
### fetch(url[, options]) | ||
## fetch(url, options) | ||
- `url` A string representing the URL for fetching | ||
- `options` [Options](#fetch-options) for the HTTP(S) request | ||
- Returns: <code>Promise<[Response](#class-response)></code> | ||
Returns a `Promise` | ||
Perform an HTTP(S) fetch. | ||
### Url | ||
`url` should be an absolute url, such as `http://example.com/`. A path-relative URL (`/file/under/root`) or protocol-relative URL (`//can-be-http-or-https.com/`) will result in a rejected promise. | ||
Should be an absolute url, eg `http://example.com/` | ||
<a id="fetch-options"></a> | ||
#### Options | ||
### Options | ||
The default values are shown after each option key. | ||
Note that only `method`, `headers`, `redirect` and `body` are allowed in `window.fetch`. Other options are node.js extensions. The default values are shown after each option key. | ||
```js | ||
``` | ||
{ | ||
// These properties are part of the Fetch Standard | ||
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 | ||
// 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) | ||
compress: true, // support gzip/deflate content encoding. false to disable | ||
size: 0, // maximum response body size in bytes. 0 to disable | ||
agent: null // http(s).Agent instance, allows custom proxy, certificate etc. | ||
method: 'GET' | ||
, headers: {} // request header. format {a:'1'} or {b:['1','2','3']} | ||
, redirect: 'follow' // set to `manual` to extract redirect headers, `error` to reject redirect | ||
, 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) | ||
, compress: true // support gzip/deflate content encoding. false to disable | ||
, size: 0 // maximum response body size in bytes. 0 to disable | ||
, body: empty // request body. can be a string, buffer, readable stream | ||
, agent: null // http.Agent instance, allows custom proxy, certificate etc. | ||
} | ||
``` | ||
##### Default Headers | ||
If no values are set, the following request headers will be sent automatically: | ||
# License | ||
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)_ | ||
`User-Agent` | `node-fetch/1.0 (+https://github.com/bitinn/node-fetch)` | ||
<a id="class-request"></a> | ||
### Class: Request | ||
An HTTP(S) request containing information about URL, method, headers, and the body. This class implements the [Body](#iface-body) interface. | ||
Due to the nature of Node.js, the following properties are not implemented at this moment: | ||
- `type` | ||
- `destination` | ||
- `referrer` | ||
- `referrerPolicy` | ||
- `mode` | ||
- `credentials` | ||
- `cache` | ||
- `integrity` | ||
- `keepalive` | ||
The following node-fetch extension properties are provided: | ||
- `follow` | ||
- `compress` | ||
- `counter` | ||
- `agent` | ||
See [options](#fetch-options) for exact meaning of these extensions. | ||
#### new Request(input[, options]) | ||
<small>*(spec-compliant)*</small> | ||
- `input` A string representing a URL, or another `Request` (which will be cloned) | ||
- `options` [Options][#fetch-options] for the HTTP(S) request | ||
Constructs a new `Request` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request). | ||
In most cases, directly `fetch(url, options)` is simpler than creating a `Request` object. | ||
<a id="class-response"></a> | ||
### Class: Response | ||
An HTTP(S) response. This class implements the [Body](#iface-body) interface. | ||
The following properties are not implemented in node-fetch at this moment: | ||
- `Response.error()` | ||
- `Response.redirect()` | ||
- `type` | ||
- `redirected` | ||
- `trailer` | ||
#### new Response([body[, options]]) | ||
<small>*(spec-compliant)*</small> | ||
- `body` A string or [Readable stream][node-readable] | ||
- `options` A [`ResponseInit`][response-init] options dictionary | ||
Constructs a new `Response` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response). | ||
Because Node.js does not implement service workers (for which this class was designed), one rarely has to construct a `Response` directly. | ||
<a id="class-headers"></a> | ||
### Class: Headers | ||
This class allows manipulating and iterating over a set of HTTP headers. All methods specified in the [Fetch Standard][whatwg-fetch] are implemented. | ||
#### new Headers([init]) | ||
<small>*(spec-compliant)*</small> | ||
- `init` Optional argument to pre-fill the `Headers` object | ||
Construct a new `Headers` object. `init` can be either `null`, a `Headers` object, an key-value map object, or any iterable object. | ||
```js | ||
// Example adapted from https://fetch.spec.whatwg.org/#example-headers-class | ||
const meta = { | ||
'Content-Type': 'text/xml', | ||
'Breaking-Bad': '<3' | ||
}; | ||
const headers = new Headers(meta); | ||
// The above is equivalent to | ||
const meta = [ | ||
[ 'Content-Type', 'text/xml' ], | ||
[ 'Breaking-Bad', '<3' ] | ||
]; | ||
const headers = new Headers(meta); | ||
// You can in fact use any iterable objects, like a Map or even another Headers | ||
const meta = new Map(); | ||
meta.set('Content-Type', 'text/xml'); | ||
meta.set('Breaking-Bad', '<3'); | ||
const headers = new Headers(meta); | ||
const copyOfHeaders = new Headers(headers); | ||
``` | ||
<a id="iface-body"></a> | ||
### Interface: Body | ||
`Body` is an abstract interface with methods that are applicable to both `Request` and `Response` classes. | ||
The following methods are not yet implemented in node-fetch at this moment: | ||
- `formData()` | ||
#### body.body | ||
<small>*(deviation from spec)*</small> | ||
* Node.js [`Readable` stream][node-readable] | ||
The data encapsulated in the `Body` object. Note that while the [Fetch Standard][whatwg-fetch] requires the property to always be a WHATWG `ReadableStream`, in node-fetch it is a Node.js [`Readable` stream][node-readable]. | ||
#### body.bodyUsed | ||
<small>*(spec-compliant)*</small> | ||
* `Boolean` | ||
A boolean property for if this body has been consumed. Per spec, a consumed body cannot be used again. | ||
#### body.arrayBuffer() | ||
#### body.blob() | ||
#### body.json() | ||
#### body.text() | ||
<small>*(spec-compliant)*</small> | ||
* Returns: <code>Promise</code> | ||
Consume the body and return a promise that will resolve to one of these formats. | ||
#### body.buffer() | ||
<small>*(node-fetch extension)*</small> | ||
* Returns: <code>Promise<Buffer></code> | ||
Consume the body and return a promise that will resolve to a Buffer. | ||
#### body.textConverted() | ||
<small>*(node-fetch extension)*</small> | ||
* Returns: <code>Promise<String></code> | ||
Identical to `body.text()`, except instead of always converting to UTF-8, encoding sniffing will be performed and text converted to UTF-8, if possible. | ||
<a id="class-fetcherror"></a> | ||
### Class: FetchError | ||
<small>*(node-fetch extension)*</small> | ||
An operational error in the fetching process. See [ERROR-HANDLING.md][] for more info. | ||
## License | ||
MIT | ||
## Acknowledgement | ||
# Acknowledgement | ||
@@ -387,6 +211,1 @@ Thanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference. | ||
[codecov-url]: https://codecov.io/gh/bitinn/node-fetch | ||
[ERROR-HANDLING.md]: https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md | ||
[whatwg-fetch]: https://fetch.spec.whatwg.org/ | ||
[response-init]: https://fetch.spec.whatwg.org/#responseinit | ||
[node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams | ||
[mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers |
{ | ||
"name": "make-fetch-happen", | ||
"version": "2.2.2", | ||
"version": "2.2.3", | ||
"description": "Opinionated, caching, retrying fetch client", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# make-fetch-happen [![npm version](https://img.shields.io/npm/v/make-fetch-happen.svg)](https://npm.im/make-fetch-happen) [![license](https://img.shields.io/npm/l/make-fetch-happen.svg)](https://npm.im/make-fetch-happen) [![Travis](https://img.shields.io/travis/zkat/make-fetch-happen.svg)](https://travis-ci.org/zkat/make-fetch-happen) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/make-fetch-happen?svg=true)](https://ci.appveyor.com/project/zkat/make-fetch-happen) [![Coverage Status](https://coveralls.io/repos/github/zkat/make-fetch-happen/badge.svg?branch=latest)](https://coveralls.io/github/zkat/make-fetch-happen?branch=latest) | ||
[`make-fetch-happen`](https://github.com/zkat/make-fetch-happen) is a Node.js library that implements the [`fetch` API](https://fetch.spec.whatwg.org/), including HTTP Cache support, request pooling, proxies, retries, [and more](#features)! | ||
[`make-fetch-happen`](https://github.com/zkat/make-fetch-happen) is a Node.js | ||
library that wraps [`node-fetch`](https://npm.im/node-fetch) with additional | ||
features it doesn't intend to include, including HTTP Cache support, request | ||
pooling, proxies, retries, [and more](#features)! | ||
@@ -53,3 +56,3 @@ ## Install | ||
* Follows `fetch` spec, using [`node-fetch`](https://npm.im/node-fetch) under the hood. | ||
* Builds around [`node-fetch`](https://npm.im/node-fetch) for the core [`fetch` API](https://fetch.spec.whatwg.org) implementation | ||
* Request pooling out of the box | ||
@@ -56,0 +59,0 @@ * Quite fast, really |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2532140
1656
42181
374
8
43