node-fetch
Advanced tools
Comparing version 2.0.0-alpha.3 to 2.0.0-alpha.4
@@ -8,30 +8,34 @@ | ||
## v2.0.0-alpha.3 | ||
## v2.0.0 | ||
- Major: overwrite user's `Content-Length` if we can be sure our information is correct | ||
- Fix: exhaust list in `Headers` constructor before processing | ||
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. | ||
## v2.0.0-alpha.2 | ||
### General changes | ||
- 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 | ||
- 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 | ||
## v2.0.0-alpha.1 | ||
### HTTP requests | ||
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 (per spec) | ||
- Fix: support WHATWG URL objects, created by `whatwg-url` package or `require('url').URL` in Node.js 7+ | ||
- Major: Node.js 0.10.x support is dropped | ||
- Major: rewrite in transpiled ES2015 | ||
### 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: internal methods are no longer exposed | ||
- 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 | ||
- Major: throw error when a `GET` or `HEAD` Request is constructed with a non-null body (per spec) | ||
- 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 | ||
@@ -41,7 +45,9 @@ - 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 | ||
@@ -48,0 +54,0 @@ |
@@ -1,26 +0,8 @@ | ||
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 * as https from 'https'; | ||
import { createGunzip, createInflate, createInflateRaw } from 'zlib'; | ||
import { Z_SYNC_FLUSH, createGunzip, createInflate, createInflateRaw } from 'zlib'; | ||
import * as zlib from 'zlib'; | ||
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 Stream, { PassThrough } from 'stream'; | ||
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'; | ||
@@ -30,11 +12,9 @@ // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js | ||
var BUFFER = _Symbol('buffer'); | ||
var TYPE = _Symbol('type'); | ||
var CLOSED = _Symbol('closed'); | ||
const BUFFER = Symbol('buffer'); | ||
const TYPE = Symbol('type'); | ||
const CLOSED = Symbol('closed'); | ||
var Blob = function () { | ||
function Blob() { | ||
_classCallCheck(this, Blob); | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
class Blob { | ||
constructor() { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Blob', | ||
@@ -49,13 +29,13 @@ writable: false, | ||
var blobParts = arguments[0]; | ||
var options = arguments[1]; | ||
const blobParts = arguments[0]; | ||
const options = arguments[1]; | ||
var buffers = []; | ||
const buffers = []; | ||
if (blobParts) { | ||
var a = blobParts; | ||
var length = Number(a.length); | ||
for (var i = 0; i < length; i++) { | ||
var element = a[i]; | ||
var buffer = void 0; | ||
const a = blobParts; | ||
const length = Number(a.length); | ||
for (let i = 0; i < length; i++) { | ||
const element = a[i]; | ||
let buffer; | ||
if (element instanceof Buffer) { | ||
@@ -78,3 +58,3 @@ buffer = element; | ||
var type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
let type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
if (type && !/[^\u0020-\u007E]/.test(type)) { | ||
@@ -84,10 +64,17 @@ 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; | ||
Blob.prototype.slice = function slice() { | ||
var size = this.size; | ||
var start = arguments[0]; | ||
var end = arguments[1]; | ||
var relativeStart = void 0, | ||
relativeEnd = void 0; | ||
const start = arguments[0]; | ||
const end = arguments[1]; | ||
let relativeStart, relativeEnd; | ||
if (start === undefined) { | ||
@@ -107,37 +94,17 @@ relativeStart = 0; | ||
} | ||
var span = Math.max(relativeEnd - relativeStart, 0); | ||
const span = Math.max(relativeEnd - relativeStart, 0); | ||
var buffer = this[BUFFER]; | ||
var slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
var blob = new Blob([], { type: arguments[2] }); | ||
const buffer = this[BUFFER]; | ||
const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
const blob = new Blob([], { type: arguments[2] }); | ||
blob[BUFFER] = slicedBuffer; | ||
blob[CLOSED] = this[CLOSED]; | ||
return blob; | ||
}; | ||
Blob.prototype.close = function close() { | ||
} | ||
close() { | ||
this[CLOSED] = true; | ||
}; | ||
} | ||
} | ||
_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, { | ||
Object.defineProperty(Blob.prototype, Symbol.toStringTag, { | ||
value: 'BlobPrototype', | ||
@@ -178,3 +145,3 @@ writable: false, | ||
FetchError.prototype = _Object$create(Error.prototype); | ||
FetchError.prototype = Object.create(Error.prototype); | ||
FetchError.prototype.constructor = FetchError; | ||
@@ -189,4 +156,3 @@ FetchError.prototype.name = 'FetchError'; | ||
var DISTURBED = _Symbol('disturbed'); | ||
var CONSUME_BODY = _Symbol('consumeBody'); | ||
const DISTURBED = Symbol('disturbed'); | ||
@@ -196,2 +162,4 @@ /** | ||
* | ||
* Cannot use ES6 class because Body must be called with .call(). | ||
* | ||
* @param Stream body Readable stream | ||
@@ -201,35 +169,37 @@ * @param Object opts Response options | ||
*/ | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size; | ||
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; | ||
let size = _ref$size === undefined ? 0 : _ref$size; | ||
var _ref$timeout = _ref.timeout; | ||
let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
_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; | ||
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); | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
Body.prototype = { | ||
get bodyUsed() { | ||
return this[DISTURBED]; | ||
}, | ||
/** | ||
@@ -240,7 +210,7 @@ * Decode response as ArrayBuffer | ||
*/ | ||
Body.prototype.arrayBuffer = function arrayBuffer() { | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
return toArrayBuffer(buf); | ||
arrayBuffer() { | ||
return consumeBody.call(this).then(function (buf) { | ||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); | ||
}); | ||
}; | ||
}, | ||
@@ -252,16 +222,14 @@ /** | ||
*/ | ||
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( | ||
blob() { | ||
let ct = this.headers && this.headers.get('content-type') || ''; | ||
return consumeBody.call(this).then(function (buf) { | ||
return Object.assign( | ||
// Prevent copying | ||
new Blob([], { | ||
type: ct.toLowerCase() | ||
}), (_Object$assign2 = {}, _Object$assign2[BUFFER] = buf, _Object$assign2)); | ||
}), { | ||
[BUFFER]: buf | ||
}); | ||
}); | ||
}; | ||
}, | ||
@@ -273,9 +241,7 @@ /** | ||
*/ | ||
Body.prototype.json = function json() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
json() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return JSON.parse(buffer.toString()); | ||
}); | ||
}; | ||
}, | ||
@@ -287,9 +253,7 @@ /** | ||
*/ | ||
Body.prototype.text = function text() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
text() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return buffer.toString(); | ||
}); | ||
}; | ||
}, | ||
@@ -301,8 +265,6 @@ /** | ||
*/ | ||
buffer() { | ||
return consumeBody.call(this); | ||
}, | ||
Body.prototype.buffer = function buffer() { | ||
return this[CONSUME_BODY](); | ||
}; | ||
/** | ||
@@ -314,116 +276,121 @@ * Decode response as text, while automatically detecting the encoding and | ||
*/ | ||
Body.prototype.textConverted = function textConverted() { | ||
textConverted() { | ||
var _this = this; | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return convertBody(buffer, _this.headers); | ||
}); | ||
}; | ||
} | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
}; | ||
Body.prototype[CONSUME_BODY] = function () { | ||
var _this2 = this; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error('body used already for: ' + this.url)); | ||
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); | ||
} | ||
} | ||
}; | ||
this[DISTURBED] = true; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
function consumeBody(body) { | ||
var _this2 = this; | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error(`body used already for: ${this.url}`)); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
this[DISTURBED] = true; | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// istanbul ignore if: should never happen | ||
if (!bodyStream(this.body)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
var accum = []; | ||
var accumBytes = 0; | ||
var abort = false; | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
var resTimeout = void 0; | ||
// istanbul ignore if: should never happen | ||
if (!(this.body instanceof Stream)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// 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); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
let accum = []; | ||
let accumBytes = 0; | ||
let abort = false; | ||
// 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)); | ||
}); | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
let resTimeout; | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
// 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); | ||
} | ||
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; | ||
} | ||
// 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)); | ||
}); | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
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; | ||
} | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
}; | ||
_createClass(Body, [{ | ||
key: 'bodyUsed', | ||
get: function get() { | ||
return this[DISTURBED]; | ||
} | ||
}]); | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
return Body; | ||
}(); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* 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) { | ||
var ct = headers.get('content-type'); | ||
var charset = 'utf-8'; | ||
var res = void 0, | ||
str = void 0; | ||
const ct = headers.get('content-type'); | ||
let charset = 'utf-8'; | ||
let res, str; | ||
@@ -478,6 +445,5 @@ // header | ||
*/ | ||
function clone$1(instance) { | ||
var p1 = void 0, | ||
p2 = void 0; | ||
var body = instance.body; | ||
function clone(instance) { | ||
let p1, p2; | ||
let body = instance.body; | ||
@@ -491,3 +457,3 @@ // don't allow cloning a used body | ||
// note: we can't clone the form-data object without having it as a dependency | ||
if (bodyStream(body) && typeof body.getBoundary !== 'function') { | ||
if (body instanceof Stream && typeof body.getBoundary !== 'function') { | ||
// tee instance body | ||
@@ -516,3 +482,3 @@ p1 = new PassThrough(); | ||
function extractContentType(instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
@@ -536,3 +502,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 { | ||
@@ -546,4 +512,5 @@ // body is stream | ||
function getTotalBytes(instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
// istanbul ignore if: included for completion | ||
@@ -578,3 +545,3 @@ if (body === null) { | ||
function writeToStream(dest, instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
@@ -651,3 +618,3 @@ | ||
if (!isValidTokenChar(val.charCodeAt(0))) return false; | ||
var len = val.length; | ||
const len = val.length; | ||
if (len > 1) { | ||
@@ -705,3 +672,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`); | ||
} | ||
@@ -714,3 +681,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`); | ||
} | ||
@@ -720,5 +687,4 @@ return value; | ||
var MAP = _Symbol('map'); | ||
var Headers = function () { | ||
const MAP = Symbol('map'); | ||
class Headers { | ||
/** | ||
@@ -730,9 +696,20 @@ * Headers class | ||
*/ | ||
function Headers() { | ||
var init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
constructor() { | ||
let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
_classCallCheck(this, Headers); | ||
this[MAP] = Object.create(null); | ||
this[MAP] = _Object$create(null); | ||
if (init instanceof Headers) { | ||
const rawHeaders = init.raw(); | ||
const headerNames = Object.keys(rawHeaders); | ||
for (const headerName of headerNames) { | ||
for (const value of rawHeaders[headerName]) { | ||
this.append(headerName, value); | ||
} | ||
} | ||
return; | ||
} | ||
// We don't worry about converting prop to ByteString here as append() | ||
@@ -743,3 +720,3 @@ // will handle it. | ||
} else if (typeof init === 'object') { | ||
var method = init[_Symbol$iterator]; | ||
const method = init[Symbol.iterator]; | ||
if (method != null) { | ||
@@ -752,59 +729,20 @@ if (typeof method !== 'function') { | ||
// Note: per spec we have to first exhaust the lists then process them | ||
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') { | ||
const pairs = []; | ||
for (const pair of init) { | ||
if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { | ||
throw new TypeError('Each header pair must be iterable'); | ||
} | ||
pairs.push(_Array$from(pair)); | ||
pairs.push(Array.from(pair)); | ||
} | ||
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) { | ||
for (const pair of pairs) { | ||
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 (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]; | ||
for (const key of Object.keys(init)) { | ||
const value = init[key]; | ||
this.append(key, value); | ||
@@ -817,3 +755,3 @@ } | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Headers', | ||
@@ -832,6 +770,4 @@ writable: false, | ||
*/ | ||
Headers.prototype.get = function get(name) { | ||
var list = this[MAP][sanitizeName(name)]; | ||
get(name) { | ||
const list = this[MAP][sanitizeName(name)]; | ||
if (!list) { | ||
@@ -841,4 +777,4 @@ return null; | ||
return list.join(','); | ||
}; | ||
return list.join(', '); | ||
} | ||
@@ -852,13 +788,11 @@ /** | ||
*/ | ||
forEach(callback) { | ||
let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
Headers.prototype.forEach = function forEach(callback) { | ||
var thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
var pairs = getHeaderPairs(this); | ||
var i = 0; | ||
let pairs = getHeaderPairs(this); | ||
let i = 0; | ||
while (i < pairs.length) { | ||
var _pairs$i = pairs[i], | ||
name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
var _pairs$i = pairs[i]; | ||
const name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
@@ -869,3 +803,3 @@ callback.call(thisArg, value, name, this); | ||
} | ||
}; | ||
} | ||
@@ -879,7 +813,5 @@ /** | ||
*/ | ||
Headers.prototype.set = function set(name, value) { | ||
set(name, value) { | ||
this[MAP][sanitizeName(name)] = [sanitizeValue(value)]; | ||
}; | ||
} | ||
@@ -893,5 +825,3 @@ /** | ||
*/ | ||
Headers.prototype.append = function append(name, value) { | ||
append(name, value) { | ||
if (!this.has(name)) { | ||
@@ -903,3 +833,3 @@ this.set(name, value); | ||
this[MAP][sanitizeName(name)].push(sanitizeValue(value)); | ||
}; | ||
} | ||
@@ -912,7 +842,5 @@ /** | ||
*/ | ||
Headers.prototype.has = function has(name) { | ||
has(name) { | ||
return !!this[MAP][sanitizeName(name)]; | ||
}; | ||
} | ||
@@ -925,7 +853,5 @@ /** | ||
*/ | ||
Headers.prototype.delete = function _delete(name) { | ||
delete(name) { | ||
delete this[MAP][sanitizeName(name)]; | ||
}; | ||
} | ||
@@ -937,5 +863,5 @@ /** | ||
*/ | ||
Headers.prototype.raw = function raw() { | ||
raw() { | ||
return this[MAP]; | ||
}; | ||
} | ||
@@ -947,7 +873,5 @@ /** | ||
*/ | ||
Headers.prototype.keys = function keys() { | ||
keys() { | ||
return createHeadersIterator(this, 'key'); | ||
}; | ||
} | ||
@@ -959,7 +883,5 @@ /** | ||
*/ | ||
Headers.prototype.values = function values() { | ||
values() { | ||
return createHeadersIterator(this, 'value'); | ||
}; | ||
} | ||
@@ -973,14 +895,9 @@ /** | ||
*/ | ||
Headers.prototype[_Symbol$iterator] = function () { | ||
[Symbol.iterator]() { | ||
return createHeadersIterator(this, 'key+value'); | ||
}; | ||
} | ||
} | ||
Headers.prototype.entries = Headers.prototype[Symbol.iterator]; | ||
return Headers; | ||
}(); | ||
Headers.prototype.entries = Headers.prototype[_Symbol$iterator]; | ||
_Object$defineProperty(Headers.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Headers.prototype, Symbol.toStringTag, { | ||
value: 'HeadersPrototype', | ||
@@ -993,3 +910,3 @@ writable: false, | ||
function getHeaderPairs(headers, kind) { | ||
var keys = _Object$keys(headers[MAP]).sort(); | ||
const keys = Object.keys(headers[MAP]).sort(); | ||
return keys.map(kind === 'key' ? function (k) { | ||
@@ -1002,9 +919,9 @@ return [k]; | ||
var INTERNAL = _Symbol('internal'); | ||
const INTERNAL = Symbol('internal'); | ||
function createHeadersIterator(target, kind) { | ||
var iterator = _Object$create(HeadersIteratorPrototype); | ||
const iterator = Object.create(HeadersIteratorPrototype); | ||
iterator[INTERNAL] = { | ||
target: target, | ||
kind: kind, | ||
target, | ||
kind, | ||
index: 0 | ||
@@ -1015,16 +932,16 @@ }; | ||
var HeadersIteratorPrototype = _Object$setPrototypeOf({ | ||
next: function next() { | ||
const HeadersIteratorPrototype = Object.setPrototypeOf({ | ||
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], | ||
target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var _INTERNAL = this[INTERNAL]; | ||
const target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var values = getHeaderPairs(target, kind); | ||
var len = values.length; | ||
const values = getHeaderPairs(target, kind); | ||
const len = values.length; | ||
if (index >= len) { | ||
@@ -1037,6 +954,6 @@ return { | ||
var pair = values[index]; | ||
const pair = values[index]; | ||
this[INTERNAL].index = index + 1; | ||
var result = void 0; | ||
let result; | ||
if (kind === 'key') { | ||
@@ -1055,12 +972,5 @@ result = pair[0]; | ||
} | ||
}, _Object$getPrototypeOf(_Object$getPrototypeOf(_getIterator([])))); | ||
}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); | ||
// 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, { | ||
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { | ||
value: 'HeadersIterator', | ||
@@ -1085,20 +995,16 @@ 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] : {}; | ||
var Response = function (_Body) { | ||
_inherits(Response, _Body); | ||
Body.call(this, body, opts); | ||
function Response() { | ||
var body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.url = opts.url; | ||
this.status = opts.status || 200; | ||
this.statusText = opts.statusText || STATUS_CODES[this.status]; | ||
_classCallCheck(this, Response); | ||
this.headers = new Headers(opts.headers); | ||
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, { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Response', | ||
@@ -1109,3 +1015,2 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
@@ -1116,4 +1021,6 @@ | ||
*/ | ||
get ok() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
/** | ||
@@ -1124,5 +1031,5 @@ * Clone this response | ||
*/ | ||
Response.prototype.clone = function clone() { | ||
clone() { | ||
return new Response(clone$1(this), { | ||
return new Response(clone(this), { | ||
url: this.url, | ||
@@ -1134,15 +1041,8 @@ status: this.status, | ||
}); | ||
}; | ||
} | ||
} | ||
_createClass(Response, [{ | ||
key: 'ok', | ||
get: function get() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
}]); | ||
Body.mixIn(Response.prototype); | ||
return Response; | ||
}(Body); | ||
_Object$defineProperty(Response.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Response.prototype, Symbol.toStringTag, { | ||
value: 'ResponsePrototype', | ||
@@ -1160,3 +1060,3 @@ writable: false, | ||
var PARSED_URL = _Symbol('url'); | ||
const PARSED_URL = Symbol('url'); | ||
@@ -1170,13 +1070,8 @@ /** | ||
*/ | ||
class Request { | ||
constructor(input) { | ||
let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var Request = function (_Body) { | ||
_inherits(Request, _Body); | ||
let parsedURL; | ||
function Request(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, Request); | ||
var parsedURL = void 0; | ||
// normalize input | ||
@@ -1191,3 +1086,3 @@ if (!(input instanceof Request)) { | ||
// coerce input to a string before attempting to parse | ||
parsedURL = parse('' + input); | ||
parsedURL = parse(`${input}`); | ||
} | ||
@@ -1199,3 +1094,3 @@ input = {}; | ||
var method = init.method || input.method || 'GET'; | ||
let method = init.method || input.method || 'GET'; | ||
@@ -1206,18 +1101,18 @@ if ((init.body != null || input instanceof Request && input.body !== null) && (method === 'GET' || method === 'HEAD')) { | ||
var inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone$1(input) : null; | ||
let inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone(input) : null; | ||
// fetch spec options | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, inputBody, { | ||
Body.call(this, inputBody, { | ||
timeout: init.timeout || input.timeout || 0, | ||
size: init.size || input.size || 0 | ||
})); | ||
}); | ||
_this.method = method.toUpperCase(); | ||
_this.redirect = init.redirect || input.redirect || 'follow'; | ||
_this.headers = new Headers(init.headers || input.headers || {}); | ||
// fetch spec options | ||
this.method = method.toUpperCase(); | ||
this.redirect = init.redirect || input.redirect || 'follow'; | ||
this.headers = new Headers(init.headers || input.headers || {}); | ||
if (init.body != null) { | ||
var contentType = extractContentType(_this); | ||
if (contentType !== null && !_this.headers.has('Content-Type')) { | ||
_this.headers.append('Content-Type', contentType); | ||
const contentType = extractContentType(this); | ||
if (contentType !== null && !this.headers.has('Content-Type')) { | ||
this.headers.append('Content-Type', contentType); | ||
} | ||
@@ -1227,9 +1122,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', | ||
@@ -1240,5 +1135,8 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
get url() { | ||
return format(this[PARSED_URL]); | ||
} | ||
/** | ||
@@ -1249,17 +1147,10 @@ * Clone this request | ||
*/ | ||
Request.prototype.clone = function clone$1() { | ||
clone() { | ||
return new Request(this); | ||
}; | ||
} | ||
} | ||
_createClass(Request, [{ | ||
key: 'url', | ||
get: function get() { | ||
return format(this[PARSED_URL]); | ||
} | ||
}]); | ||
Body.mixIn(Request.prototype); | ||
return Request; | ||
}(Body); | ||
_Object$defineProperty(Request.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Request.prototype, Symbol.toStringTag, { | ||
value: 'RequestPrototype', | ||
@@ -1272,3 +1163,4 @@ writable: false, | ||
function getNodeRequestOptions(request) { | ||
var headers = new Headers(request.headers); | ||
const parsedURL = request[PARSED_URL]; | ||
const headers = new Headers(request.headers); | ||
@@ -1281,8 +1173,12 @@ // fetch step 3 | ||
// Basic fetch | ||
if (!/^https?:$/.test(request[PARSED_URL].protocol)) { | ||
throw new Error('only http(s) protocols are supported'); | ||
if (!parsedURL.protocol || !parsedURL.hostname) { | ||
throw new TypeError('Only absolute URLs are supported'); | ||
} | ||
if (!/^https?:$/.test(parsedURL.protocol)) { | ||
throw new TypeError('Only HTTP(S) protocols are supported'); | ||
} | ||
// HTTP-network-or-cache fetch steps 5-9 | ||
var contentLengthValue = null; | ||
let contentLengthValue = null; | ||
if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { | ||
@@ -1292,3 +1188,3 @@ contentLengthValue = '0'; | ||
if (request.body != null) { | ||
var totalBytes = getTotalBytes(request); | ||
const totalBytes = getTotalBytes(request); | ||
if (typeof totalBytes === 'number') { | ||
@@ -1318,3 +1214,3 @@ contentLengthValue = String(totalBytes); | ||
return _Object$assign({}, request[PARSED_URL], { | ||
return Object.assign({}, parsedURL, { | ||
method: request.method, | ||
@@ -1351,12 +1247,7 @@ headers: headers.raw(), | ||
// build request object | ||
var request = new Request(url$$1, opts); | ||
const request = new Request(url$$1, opts); | ||
const options = getNodeRequestOptions(request); | ||
var options = getNodeRequestOptions(request); | ||
const send = (options.protocol === 'https:' ? https : http).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 | ||
@@ -1368,4 +1259,4 @@ if (options.headers.host) { | ||
// send request | ||
var req = send(options); | ||
var reqTimeout = void 0; | ||
const req = send(options); | ||
let reqTimeout; | ||
@@ -1376,3 +1267,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); | ||
@@ -1384,3 +1275,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)); | ||
}); | ||
@@ -1394,3 +1285,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; | ||
@@ -1400,3 +1291,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; | ||
@@ -1406,3 +1297,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; | ||
@@ -1425,36 +1316,10 @@ } | ||
// normalize location header for manual redirect mode | ||
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); | ||
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); | ||
} | ||
} else { | ||
headers.append(_name, res.headers[_name]); | ||
headers.append(name, res.headers[name]); | ||
} | ||
@@ -1467,4 +1332,4 @@ } | ||
// prepare response | ||
var body = res.pipe(new PassThrough()); | ||
var response_options = { | ||
let body = res.pipe(new PassThrough()); | ||
const response_options = { | ||
url: request.url, | ||
@@ -1478,32 +1343,40 @@ status: res.statusCode, | ||
// response object | ||
var output = void 0; | ||
// HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); | ||
// 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' || !headers.has('content-encoding') || res.statusCode === 204 || res.statusCode === 304) { | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { | ||
resolve$$1(new Response(body, response_options)); | ||
return; | ||
} | ||
// otherwise, check for gzip or deflate | ||
var name = headers.get('content-encoding'); | ||
// For Node v6+ | ||
// 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 | ||
}; | ||
// for gzip | ||
if (name == 'gzip' || name == 'x-gzip') { | ||
body = body.pipe(createGunzip()); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
if (codings == 'gzip' || codings == 'x-gzip') { | ||
body = body.pipe(createGunzip(zlibOptions)); | ||
resolve$$1(new Response(body, response_options)); | ||
return; | ||
} | ||
// for deflate | ||
} else if (name == 'deflate' || name == 'x-deflate') { | ||
// for deflate | ||
if (codings == 'deflate' || codings == 'x-deflate') { | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
var raw = res.pipe(new PassThrough()); | ||
const raw = res.pipe(new PassThrough()); | ||
raw.once('data', function (chunk) { | ||
@@ -1516,4 +1389,3 @@ // see http://stackoverflow.com/questions/37519828 | ||
} | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
resolve$$1(new Response(body, response_options)); | ||
}); | ||
@@ -1524,5 +1396,3 @@ return; | ||
// otherwise, use response as-is | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
resolve$$1(new Response(body, response_options)); | ||
}); | ||
@@ -1547,2 +1417,2 @@ | ||
export { Headers, Request, Response };export default fetch; | ||
export { Headers, Request, Response, FetchError };export default fetch; |
897
lib/index.js
@@ -7,4 +7,2 @@ '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'); | ||
@@ -14,19 +12,5 @@ var http = require('http'); | ||
var zlib = require('zlib'); | ||
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 Stream = require('stream'); | ||
var Stream__default = _interopDefault(Stream); | ||
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')); | ||
@@ -36,11 +20,9 @@ // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js | ||
var BUFFER = _Symbol('buffer'); | ||
var TYPE = _Symbol('type'); | ||
var CLOSED = _Symbol('closed'); | ||
const BUFFER = Symbol('buffer'); | ||
const TYPE = Symbol('type'); | ||
const CLOSED = Symbol('closed'); | ||
var Blob = function () { | ||
function Blob() { | ||
_classCallCheck(this, Blob); | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
class Blob { | ||
constructor() { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Blob', | ||
@@ -55,13 +37,13 @@ writable: false, | ||
var blobParts = arguments[0]; | ||
var options = arguments[1]; | ||
const blobParts = arguments[0]; | ||
const options = arguments[1]; | ||
var buffers = []; | ||
const buffers = []; | ||
if (blobParts) { | ||
var a = blobParts; | ||
var length = Number(a.length); | ||
for (var i = 0; i < length; i++) { | ||
var element = a[i]; | ||
var buffer = void 0; | ||
const a = blobParts; | ||
const length = Number(a.length); | ||
for (let i = 0; i < length; i++) { | ||
const element = a[i]; | ||
let buffer; | ||
if (element instanceof Buffer) { | ||
@@ -84,3 +66,3 @@ buffer = element; | ||
var type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
let type = options && options.type !== undefined && String(options.type).toLowerCase(); | ||
if (type && !/[^\u0020-\u007E]/.test(type)) { | ||
@@ -90,10 +72,17 @@ 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; | ||
Blob.prototype.slice = function slice() { | ||
var size = this.size; | ||
var start = arguments[0]; | ||
var end = arguments[1]; | ||
var relativeStart = void 0, | ||
relativeEnd = void 0; | ||
const start = arguments[0]; | ||
const end = arguments[1]; | ||
let relativeStart, relativeEnd; | ||
if (start === undefined) { | ||
@@ -113,37 +102,17 @@ relativeStart = 0; | ||
} | ||
var span = Math.max(relativeEnd - relativeStart, 0); | ||
const span = Math.max(relativeEnd - relativeStart, 0); | ||
var buffer = this[BUFFER]; | ||
var slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
var blob = new Blob([], { type: arguments[2] }); | ||
const buffer = this[BUFFER]; | ||
const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); | ||
const blob = new Blob([], { type: arguments[2] }); | ||
blob[BUFFER] = slicedBuffer; | ||
blob[CLOSED] = this[CLOSED]; | ||
return blob; | ||
}; | ||
Blob.prototype.close = function close() { | ||
} | ||
close() { | ||
this[CLOSED] = true; | ||
}; | ||
} | ||
} | ||
_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, { | ||
Object.defineProperty(Blob.prototype, Symbol.toStringTag, { | ||
value: 'BlobPrototype', | ||
@@ -184,3 +153,3 @@ writable: false, | ||
FetchError.prototype = _Object$create(Error.prototype); | ||
FetchError.prototype = Object.create(Error.prototype); | ||
FetchError.prototype.constructor = FetchError; | ||
@@ -195,4 +164,3 @@ FetchError.prototype.name = 'FetchError'; | ||
var DISTURBED = _Symbol('disturbed'); | ||
var CONSUME_BODY = _Symbol('consumeBody'); | ||
const DISTURBED = Symbol('disturbed'); | ||
@@ -202,2 +170,4 @@ /** | ||
* | ||
* Cannot use ES6 class because Body must be called with .call(). | ||
* | ||
* @param Stream body Readable stream | ||
@@ -207,35 +177,37 @@ * @param Object opts Response options | ||
*/ | ||
function Body(body) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$size = _ref.size; | ||
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; | ||
let size = _ref$size === undefined ? 0 : _ref$size; | ||
var _ref$timeout = _ref.timeout; | ||
let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; | ||
_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; | ||
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); | ||
} | ||
this.body = body; | ||
this[DISTURBED] = false; | ||
this.size = size; | ||
this.timeout = timeout; | ||
} | ||
Body.prototype = { | ||
get bodyUsed() { | ||
return this[DISTURBED]; | ||
}, | ||
/** | ||
@@ -246,7 +218,7 @@ * Decode response as ArrayBuffer | ||
*/ | ||
Body.prototype.arrayBuffer = function arrayBuffer() { | ||
return this[CONSUME_BODY]().then(function (buf) { | ||
return toArrayBuffer(buf); | ||
arrayBuffer() { | ||
return consumeBody.call(this).then(function (buf) { | ||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); | ||
}); | ||
}; | ||
}, | ||
@@ -258,16 +230,14 @@ /** | ||
*/ | ||
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( | ||
blob() { | ||
let ct = this.headers && this.headers.get('content-type') || ''; | ||
return consumeBody.call(this).then(function (buf) { | ||
return Object.assign( | ||
// Prevent copying | ||
new Blob([], { | ||
type: ct.toLowerCase() | ||
}), (_Object$assign2 = {}, _Object$assign2[BUFFER] = buf, _Object$assign2)); | ||
}), { | ||
[BUFFER]: buf | ||
}); | ||
}); | ||
}; | ||
}, | ||
@@ -279,9 +249,7 @@ /** | ||
*/ | ||
Body.prototype.json = function json() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
json() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return JSON.parse(buffer.toString()); | ||
}); | ||
}; | ||
}, | ||
@@ -293,9 +261,7 @@ /** | ||
*/ | ||
Body.prototype.text = function text() { | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
text() { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return buffer.toString(); | ||
}); | ||
}; | ||
}, | ||
@@ -307,8 +273,6 @@ /** | ||
*/ | ||
buffer() { | ||
return consumeBody.call(this); | ||
}, | ||
Body.prototype.buffer = function buffer() { | ||
return this[CONSUME_BODY](); | ||
}; | ||
/** | ||
@@ -320,116 +284,121 @@ * Decode response as text, while automatically detecting the encoding and | ||
*/ | ||
Body.prototype.textConverted = function textConverted() { | ||
textConverted() { | ||
var _this = this; | ||
return this[CONSUME_BODY]().then(function (buffer) { | ||
return consumeBody.call(this).then(function (buffer) { | ||
return convertBody(buffer, _this.headers); | ||
}); | ||
}; | ||
} | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
}; | ||
Body.prototype[CONSUME_BODY] = function () { | ||
var _this2 = this; | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error('body used already for: ' + this.url)); | ||
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); | ||
} | ||
} | ||
}; | ||
this[DISTURBED] = true; | ||
/** | ||
* Decode buffers into utf-8 string | ||
* | ||
* @return Promise | ||
*/ | ||
function consumeBody(body) { | ||
var _this2 = this; | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
if (this[DISTURBED]) { | ||
return Body.Promise.reject(new Error(`body used already for: ${this.url}`)); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
this[DISTURBED] = true; | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is null | ||
if (this.body === null) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
// body is string | ||
if (typeof this.body === 'string') { | ||
return Body.Promise.resolve(new Buffer(this.body)); | ||
} | ||
// istanbul ignore if: should never happen | ||
if (!bodyStream(this.body)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// body is blob | ||
if (this.body instanceof Blob) { | ||
return Body.Promise.resolve(this.body[BUFFER]); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
var accum = []; | ||
var accumBytes = 0; | ||
var abort = false; | ||
// body is buffer | ||
if (Buffer.isBuffer(this.body)) { | ||
return Body.Promise.resolve(this.body); | ||
} | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
var resTimeout = void 0; | ||
// istanbul ignore if: should never happen | ||
if (!(this.body instanceof Stream__default)) { | ||
return Body.Promise.resolve(new Buffer(0)); | ||
} | ||
// 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); | ||
} | ||
// body is stream | ||
// get ready to actually consume the body | ||
let accum = []; | ||
let accumBytes = 0; | ||
let abort = false; | ||
// 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)); | ||
}); | ||
return new Body.Promise(function (resolve$$1, reject) { | ||
let resTimeout; | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
// 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); | ||
} | ||
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; | ||
} | ||
// 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)); | ||
}); | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
_this2.body.on('data', function (chunk) { | ||
if (abort || chunk === null) { | ||
return; | ||
} | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
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; | ||
} | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
accumBytes += chunk.length; | ||
accum.push(chunk); | ||
}); | ||
}; | ||
_createClass(Body, [{ | ||
key: 'bodyUsed', | ||
get: function get() { | ||
return this[DISTURBED]; | ||
} | ||
}]); | ||
_this2.body.on('end', function () { | ||
if (abort) { | ||
return; | ||
} | ||
return Body; | ||
}(); | ||
clearTimeout(resTimeout); | ||
resolve$$1(Buffer.concat(accum)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* 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) { | ||
var ct = headers.get('content-type'); | ||
var charset = 'utf-8'; | ||
var res = void 0, | ||
str = void 0; | ||
const ct = headers.get('content-type'); | ||
let charset = 'utf-8'; | ||
let res, str; | ||
@@ -484,6 +453,5 @@ // header | ||
*/ | ||
function clone$1(instance) { | ||
var p1 = void 0, | ||
p2 = void 0; | ||
var body = instance.body; | ||
function clone(instance) { | ||
let p1, p2; | ||
let body = instance.body; | ||
@@ -497,6 +465,6 @@ // don't allow cloning a used body | ||
// note: we can't clone the form-data object without having it as a dependency | ||
if (bodyStream(body) && typeof body.getBoundary !== 'function') { | ||
if (body instanceof Stream__default && 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); | ||
@@ -522,3 +490,3 @@ body.pipe(p2); | ||
function extractContentType(instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
@@ -542,3 +510,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 { | ||
@@ -552,4 +520,5 @@ // body is stream | ||
function getTotalBytes(instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
// istanbul ignore if: included for completion | ||
@@ -584,3 +553,3 @@ if (body === null) { | ||
function writeToStream(dest, instance) { | ||
var body = instance.body; | ||
const body = instance.body; | ||
@@ -657,3 +626,3 @@ | ||
if (!isValidTokenChar(val.charCodeAt(0))) return false; | ||
var len = val.length; | ||
const len = val.length; | ||
if (len > 1) { | ||
@@ -711,3 +680,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`); | ||
} | ||
@@ -720,3 +689,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`); | ||
} | ||
@@ -726,5 +695,4 @@ return value; | ||
var MAP = _Symbol('map'); | ||
var Headers = function () { | ||
const MAP = Symbol('map'); | ||
class Headers { | ||
/** | ||
@@ -736,9 +704,20 @@ * Headers class | ||
*/ | ||
function Headers() { | ||
var init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
constructor() { | ||
let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
_classCallCheck(this, Headers); | ||
this[MAP] = Object.create(null); | ||
this[MAP] = _Object$create(null); | ||
if (init instanceof Headers) { | ||
const rawHeaders = init.raw(); | ||
const headerNames = Object.keys(rawHeaders); | ||
for (const headerName of headerNames) { | ||
for (const value of rawHeaders[headerName]) { | ||
this.append(headerName, value); | ||
} | ||
} | ||
return; | ||
} | ||
// We don't worry about converting prop to ByteString here as append() | ||
@@ -749,3 +728,3 @@ // will handle it. | ||
} else if (typeof init === 'object') { | ||
var method = init[_Symbol$iterator]; | ||
const method = init[Symbol.iterator]; | ||
if (method != null) { | ||
@@ -758,59 +737,20 @@ if (typeof method !== 'function') { | ||
// Note: per spec we have to first exhaust the lists then process them | ||
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') { | ||
const pairs = []; | ||
for (const pair of init) { | ||
if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { | ||
throw new TypeError('Each header pair must be iterable'); | ||
} | ||
pairs.push(_Array$from(pair)); | ||
pairs.push(Array.from(pair)); | ||
} | ||
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) { | ||
for (const pair of pairs) { | ||
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 (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]; | ||
for (const key of Object.keys(init)) { | ||
const value = init[key]; | ||
this.append(key, value); | ||
@@ -823,3 +763,3 @@ } | ||
_Object$defineProperty(this, _Symbol$toStringTag, { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Headers', | ||
@@ -838,6 +778,4 @@ writable: false, | ||
*/ | ||
Headers.prototype.get = function get(name) { | ||
var list = this[MAP][sanitizeName(name)]; | ||
get(name) { | ||
const list = this[MAP][sanitizeName(name)]; | ||
if (!list) { | ||
@@ -847,4 +785,4 @@ return null; | ||
return list.join(','); | ||
}; | ||
return list.join(', '); | ||
} | ||
@@ -858,13 +796,11 @@ /** | ||
*/ | ||
forEach(callback) { | ||
let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
Headers.prototype.forEach = function forEach(callback) { | ||
var thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; | ||
var pairs = getHeaderPairs(this); | ||
var i = 0; | ||
let pairs = getHeaderPairs(this); | ||
let i = 0; | ||
while (i < pairs.length) { | ||
var _pairs$i = pairs[i], | ||
name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
var _pairs$i = pairs[i]; | ||
const name = _pairs$i[0], | ||
value = _pairs$i[1]; | ||
@@ -875,3 +811,3 @@ callback.call(thisArg, value, name, this); | ||
} | ||
}; | ||
} | ||
@@ -885,7 +821,5 @@ /** | ||
*/ | ||
Headers.prototype.set = function set(name, value) { | ||
set(name, value) { | ||
this[MAP][sanitizeName(name)] = [sanitizeValue(value)]; | ||
}; | ||
} | ||
@@ -899,5 +833,3 @@ /** | ||
*/ | ||
Headers.prototype.append = function append(name, value) { | ||
append(name, value) { | ||
if (!this.has(name)) { | ||
@@ -909,3 +841,3 @@ this.set(name, value); | ||
this[MAP][sanitizeName(name)].push(sanitizeValue(value)); | ||
}; | ||
} | ||
@@ -918,7 +850,5 @@ /** | ||
*/ | ||
Headers.prototype.has = function has(name) { | ||
has(name) { | ||
return !!this[MAP][sanitizeName(name)]; | ||
}; | ||
} | ||
@@ -931,7 +861,5 @@ /** | ||
*/ | ||
Headers.prototype.delete = function _delete(name) { | ||
delete(name) { | ||
delete this[MAP][sanitizeName(name)]; | ||
}; | ||
} | ||
@@ -943,5 +871,5 @@ /** | ||
*/ | ||
Headers.prototype.raw = function raw() { | ||
raw() { | ||
return this[MAP]; | ||
}; | ||
} | ||
@@ -953,7 +881,5 @@ /** | ||
*/ | ||
Headers.prototype.keys = function keys() { | ||
keys() { | ||
return createHeadersIterator(this, 'key'); | ||
}; | ||
} | ||
@@ -965,7 +891,5 @@ /** | ||
*/ | ||
Headers.prototype.values = function values() { | ||
values() { | ||
return createHeadersIterator(this, 'value'); | ||
}; | ||
} | ||
@@ -979,14 +903,9 @@ /** | ||
*/ | ||
Headers.prototype[_Symbol$iterator] = function () { | ||
[Symbol.iterator]() { | ||
return createHeadersIterator(this, 'key+value'); | ||
}; | ||
} | ||
} | ||
Headers.prototype.entries = Headers.prototype[Symbol.iterator]; | ||
return Headers; | ||
}(); | ||
Headers.prototype.entries = Headers.prototype[_Symbol$iterator]; | ||
_Object$defineProperty(Headers.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Headers.prototype, Symbol.toStringTag, { | ||
value: 'HeadersPrototype', | ||
@@ -999,3 +918,3 @@ writable: false, | ||
function getHeaderPairs(headers, kind) { | ||
var keys = _Object$keys(headers[MAP]).sort(); | ||
const keys = Object.keys(headers[MAP]).sort(); | ||
return keys.map(kind === 'key' ? function (k) { | ||
@@ -1008,9 +927,9 @@ return [k]; | ||
var INTERNAL = _Symbol('internal'); | ||
const INTERNAL = Symbol('internal'); | ||
function createHeadersIterator(target, kind) { | ||
var iterator = _Object$create(HeadersIteratorPrototype); | ||
const iterator = Object.create(HeadersIteratorPrototype); | ||
iterator[INTERNAL] = { | ||
target: target, | ||
kind: kind, | ||
target, | ||
kind, | ||
index: 0 | ||
@@ -1021,16 +940,16 @@ }; | ||
var HeadersIteratorPrototype = _Object$setPrototypeOf({ | ||
next: function next() { | ||
const HeadersIteratorPrototype = Object.setPrototypeOf({ | ||
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], | ||
target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var _INTERNAL = this[INTERNAL]; | ||
const target = _INTERNAL.target, | ||
kind = _INTERNAL.kind, | ||
index = _INTERNAL.index; | ||
var values = getHeaderPairs(target, kind); | ||
var len = values.length; | ||
const values = getHeaderPairs(target, kind); | ||
const len = values.length; | ||
if (index >= len) { | ||
@@ -1043,6 +962,6 @@ return { | ||
var pair = values[index]; | ||
const pair = values[index]; | ||
this[INTERNAL].index = index + 1; | ||
var result = void 0; | ||
let result; | ||
if (kind === 'key') { | ||
@@ -1061,12 +980,5 @@ result = pair[0]; | ||
} | ||
}, _Object$getPrototypeOf(_Object$getPrototypeOf(_getIterator([])))); | ||
}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); | ||
// 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, { | ||
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { | ||
value: 'HeadersIterator', | ||
@@ -1091,20 +1003,16 @@ 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] : {}; | ||
var Response = function (_Body) { | ||
_inherits(Response, _Body); | ||
Body.call(this, body, opts); | ||
function Response() { | ||
var body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.url = opts.url; | ||
this.status = opts.status || 200; | ||
this.statusText = opts.statusText || http.STATUS_CODES[this.status]; | ||
_classCallCheck(this, Response); | ||
this.headers = new Headers(opts.headers); | ||
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, { | ||
Object.defineProperty(this, Symbol.toStringTag, { | ||
value: 'Response', | ||
@@ -1115,3 +1023,2 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
@@ -1122,4 +1029,6 @@ | ||
*/ | ||
get ok() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
/** | ||
@@ -1130,5 +1039,5 @@ * Clone this response | ||
*/ | ||
Response.prototype.clone = function clone() { | ||
clone() { | ||
return new Response(clone$1(this), { | ||
return new Response(clone(this), { | ||
url: this.url, | ||
@@ -1140,15 +1049,8 @@ status: this.status, | ||
}); | ||
}; | ||
} | ||
} | ||
_createClass(Response, [{ | ||
key: 'ok', | ||
get: function get() { | ||
return this.status >= 200 && this.status < 300; | ||
} | ||
}]); | ||
Body.mixIn(Response.prototype); | ||
return Response; | ||
}(Body); | ||
_Object$defineProperty(Response.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Response.prototype, Symbol.toStringTag, { | ||
value: 'ResponsePrototype', | ||
@@ -1166,3 +1068,3 @@ writable: false, | ||
var PARSED_URL = _Symbol('url'); | ||
const PARSED_URL = Symbol('url'); | ||
@@ -1176,13 +1078,8 @@ /** | ||
*/ | ||
class Request { | ||
constructor(input) { | ||
let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var Request = function (_Body) { | ||
_inherits(Request, _Body); | ||
let parsedURL; | ||
function Request(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, Request); | ||
var parsedURL = void 0; | ||
// normalize input | ||
@@ -1197,3 +1094,3 @@ if (!(input instanceof Request)) { | ||
// coerce input to a string before attempting to parse | ||
parsedURL = url.parse('' + input); | ||
parsedURL = url.parse(`${input}`); | ||
} | ||
@@ -1205,3 +1102,3 @@ input = {}; | ||
var method = init.method || input.method || 'GET'; | ||
let method = init.method || input.method || 'GET'; | ||
@@ -1212,18 +1109,18 @@ if ((init.body != null || input instanceof Request && input.body !== null) && (method === 'GET' || method === 'HEAD')) { | ||
var inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone$1(input) : null; | ||
let inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone(input) : null; | ||
// fetch spec options | ||
var _this = _possibleConstructorReturn(this, _Body.call(this, inputBody, { | ||
Body.call(this, inputBody, { | ||
timeout: init.timeout || input.timeout || 0, | ||
size: init.size || input.size || 0 | ||
})); | ||
}); | ||
_this.method = method.toUpperCase(); | ||
_this.redirect = init.redirect || input.redirect || 'follow'; | ||
_this.headers = new Headers(init.headers || input.headers || {}); | ||
// fetch spec options | ||
this.method = method.toUpperCase(); | ||
this.redirect = init.redirect || input.redirect || 'follow'; | ||
this.headers = new Headers(init.headers || input.headers || {}); | ||
if (init.body != null) { | ||
var contentType = extractContentType(_this); | ||
if (contentType !== null && !_this.headers.has('Content-Type')) { | ||
_this.headers.append('Content-Type', contentType); | ||
const contentType = extractContentType(this); | ||
if (contentType !== null && !this.headers.has('Content-Type')) { | ||
this.headers.append('Content-Type', contentType); | ||
} | ||
@@ -1233,9 +1130,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', | ||
@@ -1246,5 +1143,8 @@ writable: false, | ||
}); | ||
return _this; | ||
} | ||
get url() { | ||
return url.format(this[PARSED_URL]); | ||
} | ||
/** | ||
@@ -1255,17 +1155,10 @@ * Clone this request | ||
*/ | ||
Request.prototype.clone = function clone$1() { | ||
clone() { | ||
return new Request(this); | ||
}; | ||
} | ||
} | ||
_createClass(Request, [{ | ||
key: 'url', | ||
get: function get() { | ||
return url.format(this[PARSED_URL]); | ||
} | ||
}]); | ||
Body.mixIn(Request.prototype); | ||
return Request; | ||
}(Body); | ||
_Object$defineProperty(Request.prototype, _Symbol$toStringTag, { | ||
Object.defineProperty(Request.prototype, Symbol.toStringTag, { | ||
value: 'RequestPrototype', | ||
@@ -1278,3 +1171,4 @@ writable: false, | ||
function getNodeRequestOptions(request) { | ||
var headers = new Headers(request.headers); | ||
const parsedURL = request[PARSED_URL]; | ||
const headers = new Headers(request.headers); | ||
@@ -1287,8 +1181,12 @@ // fetch step 3 | ||
// Basic fetch | ||
if (!/^https?:$/.test(request[PARSED_URL].protocol)) { | ||
throw new Error('only http(s) protocols are supported'); | ||
if (!parsedURL.protocol || !parsedURL.hostname) { | ||
throw new TypeError('Only absolute URLs are supported'); | ||
} | ||
if (!/^https?:$/.test(parsedURL.protocol)) { | ||
throw new TypeError('Only HTTP(S) protocols are supported'); | ||
} | ||
// HTTP-network-or-cache fetch steps 5-9 | ||
var contentLengthValue = null; | ||
let contentLengthValue = null; | ||
if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { | ||
@@ -1298,3 +1196,3 @@ contentLengthValue = '0'; | ||
if (request.body != null) { | ||
var totalBytes = getTotalBytes(request); | ||
const totalBytes = getTotalBytes(request); | ||
if (typeof totalBytes === 'number') { | ||
@@ -1324,3 +1222,3 @@ contentLengthValue = String(totalBytes); | ||
return _Object$assign({}, request[PARSED_URL], { | ||
return Object.assign({}, parsedURL, { | ||
method: request.method, | ||
@@ -1357,12 +1255,7 @@ headers: headers.raw(), | ||
// build request object | ||
var request = new Request(url$$1, opts); | ||
const request = new Request(url$$1, opts); | ||
const options = getNodeRequestOptions(request); | ||
var options = getNodeRequestOptions(request); | ||
const send = (options.protocol === 'https:' ? https : http).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 | ||
@@ -1374,4 +1267,4 @@ if (options.headers.host) { | ||
// send request | ||
var req = send(options); | ||
var reqTimeout = void 0; | ||
const req = send(options); | ||
let reqTimeout; | ||
@@ -1382,3 +1275,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); | ||
@@ -1390,3 +1283,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)); | ||
}); | ||
@@ -1400,3 +1293,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; | ||
@@ -1406,3 +1299,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; | ||
@@ -1412,3 +1305,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; | ||
@@ -1431,36 +1324,10 @@ } | ||
// normalize location header for manual redirect mode | ||
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); | ||
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); | ||
} | ||
} else { | ||
headers.append(_name, res.headers[_name]); | ||
headers.append(name, res.headers[name]); | ||
} | ||
@@ -1473,4 +1340,4 @@ } | ||
// prepare response | ||
var body = res.pipe(new stream.PassThrough()); | ||
var response_options = { | ||
let body = res.pipe(new Stream.PassThrough()); | ||
const response_options = { | ||
url: request.url, | ||
@@ -1484,32 +1351,40 @@ status: res.statusCode, | ||
// response object | ||
var output = void 0; | ||
// HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); | ||
// 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' || !headers.has('content-encoding') || res.statusCode === 204 || res.statusCode === 304) { | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { | ||
resolve$$1(new Response(body, response_options)); | ||
return; | ||
} | ||
// otherwise, check for gzip or deflate | ||
var name = headers.get('content-encoding'); | ||
// For Node v6+ | ||
// 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 | ||
}; | ||
// for gzip | ||
if (name == 'gzip' || name == 'x-gzip') { | ||
body = body.pipe(zlib.createGunzip()); | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
if (codings == 'gzip' || codings == 'x-gzip') { | ||
body = body.pipe(zlib.createGunzip(zlibOptions)); | ||
resolve$$1(new Response(body, response_options)); | ||
return; | ||
} | ||
// for deflate | ||
} else if (name == 'deflate' || name == 'x-deflate') { | ||
// for deflate | ||
if (codings == 'deflate' || codings == 'x-deflate') { | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
var raw = res.pipe(new stream.PassThrough()); | ||
const raw = res.pipe(new Stream.PassThrough()); | ||
raw.once('data', function (chunk) { | ||
@@ -1522,4 +1397,3 @@ // see http://stackoverflow.com/questions/37519828 | ||
} | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
resolve$$1(new Response(body, response_options)); | ||
}); | ||
@@ -1530,5 +1404,3 @@ return; | ||
// otherwise, use response as-is | ||
output = new Response(body, response_options); | ||
resolve$$1(output); | ||
return; | ||
resolve$$1(new Response(body, response_options)); | ||
}); | ||
@@ -1557,1 +1429,2 @@ | ||
exports.Response = Response; | ||
exports.FetchError = FetchError; |
{ | ||
"name": "node-fetch", | ||
"version": "2.0.0-alpha.3", | ||
"version": "2.0.0-alpha.4", | ||
"description": "A light-weight module that brings window.fetch to node.js", | ||
"main": "lib/index.js", | ||
"browser": "./browser.js", | ||
"module": "lib/index.es.js", | ||
@@ -11,2 +12,5 @@ "files": [ | ||
], | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"scripts": { | ||
@@ -35,7 +39,5 @@ "build": "cross-env BABEL_ENV=rollup rollup -c", | ||
"devDependencies": { | ||
"babel-plugin-istanbul": "^3.0.0", | ||
"babel-plugin-transform-runtime": "^6.15.0", | ||
"babel-preset-env": "^1.1.8", | ||
"babel-plugin-istanbul": "^4.0.0", | ||
"babel-preset-env": "^1.1.10", | ||
"babel-register": "^6.16.3", | ||
"bluebird": "^3.3.4", | ||
"chai": "^3.5.0", | ||
@@ -46,3 +48,3 @@ "chai-as-promised": "^6.0.0", | ||
"codecov": "^1.0.1", | ||
"cross-env": "2.0.1", | ||
"cross-env": "^3.1.4", | ||
"form-data": ">=1.0.0", | ||
@@ -55,13 +57,9 @@ "is-builtin-module": "^1.0.0", | ||
"resumer": "0.0.0", | ||
"rollup": "^0.37.0", | ||
"rollup": "^0.41.4", | ||
"rollup-plugin-babel": "^2.6.1", | ||
"rollup-plugin-node-resolve": "^2.0.0", | ||
"whatwg-url": "^4.0.0" | ||
}, | ||
"dependencies": { | ||
"babel-runtime": "^6.11.6", | ||
"buffer-to-arraybuffer": "0.0.4", | ||
"encoding": "^0.1.11", | ||
"is-stream": "^1.0.1" | ||
"encoding": "^0.1.11" | ||
} | ||
} |
241
README.md
@@ -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](https://fetch.spec.whatwg.org/) and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known difference. | ||
- Make conscious trade-off when following [whatwg fetch spec][whatwg-fetch] 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](https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md) for troubleshooting. | ||
- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors][] for troubleshooting. | ||
# Difference from client-side fetch | ||
## Difference from client-side fetch | ||
@@ -38,8 +38,10 @@ - See [Known Differences](https://github.com/bitinn/node-fetch/blob/master/LIMITS.md) for details. | ||
# Install | ||
## Install | ||
`npm install node-fetch --save` | ||
```sh | ||
$ npm install node-fetch --save | ||
``` | ||
# Usage | ||
## Usage | ||
@@ -165,37 +167,215 @@ ```javascript | ||
# API | ||
## API | ||
## fetch(url, options) | ||
### fetch(url[, options]) | ||
Returns a `Promise` | ||
- `url` A string representing the URL for fetching | ||
- `options` [Options](#fetch-options) for the HTTP(S) request | ||
- Returns: <code>Promise<[Response](#class-response)></code> | ||
### Url | ||
Perform an HTTP(S) fetch. | ||
Should be an absolute url, eg `http://example.com/` | ||
`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. | ||
### Options | ||
<a id="fetch-options"></a> | ||
#### Options | ||
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. | ||
The default values are shown after each option key. | ||
``` | ||
```js | ||
{ | ||
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. | ||
// 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. | ||
} | ||
``` | ||
##### Default Headers | ||
# License | ||
If no values are set, the following request headers will be sent automatically: | ||
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. | ||
#### response.ok | ||
Convenience property representing if the request ended normally. Will evaluate to true if the response status was greater than or equal to 200 but smaller than 300. | ||
<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 | ||
@@ -211,1 +391,6 @@ 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 |
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
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
1
19
392
20
87674
2329
- Removedbabel-runtime@^6.11.6
- Removedbuffer-to-arraybuffer@0.0.4
- Removedis-stream@^1.0.1
- Removedbabel-runtime@6.26.0(transitive)
- Removedbuffer-to-arraybuffer@0.0.4(transitive)
- Removedcore-js@2.6.12(transitive)
- Removedis-stream@1.1.0(transitive)
- Removedregenerator-runtime@0.11.1(transitive)