index.es.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550
  1. // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
  2. // (MIT licensed)
  3. const BUFFER = Symbol('buffer');
  4. const TYPE = Symbol('type');
  5. class Blob {
  6. constructor() {
  7. this[TYPE] = '';
  8. const blobParts = arguments[0];
  9. const options = arguments[1];
  10. const buffers = [];
  11. if (blobParts) {
  12. const a = blobParts;
  13. const length = Number(a.length);
  14. for (let i = 0; i < length; i++) {
  15. const element = a[i];
  16. let buffer;
  17. if (element instanceof Buffer) {
  18. buffer = element;
  19. } else if (ArrayBuffer.isView(element)) {
  20. buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
  21. } else if (element instanceof ArrayBuffer) {
  22. buffer = Buffer.from(element);
  23. } else if (element instanceof Blob) {
  24. buffer = element[BUFFER];
  25. } else {
  26. buffer = Buffer.from(typeof element === 'string' ? element : String(element));
  27. }
  28. buffers.push(buffer);
  29. }
  30. }
  31. this[BUFFER] = Buffer.concat(buffers);
  32. let type = options && options.type !== undefined && String(options.type).toLowerCase();
  33. if (type && !/[^\u0020-\u007E]/.test(type)) {
  34. this[TYPE] = type;
  35. }
  36. }
  37. get size() {
  38. return this[BUFFER].length;
  39. }
  40. get type() {
  41. return this[TYPE];
  42. }
  43. slice() {
  44. const size = this.size;
  45. const start = arguments[0];
  46. const end = arguments[1];
  47. let relativeStart, relativeEnd;
  48. if (start === undefined) {
  49. relativeStart = 0;
  50. } else if (start < 0) {
  51. relativeStart = Math.max(size + start, 0);
  52. } else {
  53. relativeStart = Math.min(start, size);
  54. }
  55. if (end === undefined) {
  56. relativeEnd = size;
  57. } else if (end < 0) {
  58. relativeEnd = Math.max(size + end, 0);
  59. } else {
  60. relativeEnd = Math.min(end, size);
  61. }
  62. const span = Math.max(relativeEnd - relativeStart, 0);
  63. const buffer = this[BUFFER];
  64. const slicedBuffer = buffer.slice(relativeStart, relativeStart + span);
  65. const blob = new Blob([], { type: arguments[2] });
  66. blob[BUFFER] = slicedBuffer;
  67. return blob;
  68. }
  69. }
  70. Object.defineProperties(Blob.prototype, {
  71. size: { enumerable: true },
  72. type: { enumerable: true },
  73. slice: { enumerable: true }
  74. });
  75. Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
  76. value: 'Blob',
  77. writable: false,
  78. enumerable: false,
  79. configurable: true
  80. });
  81. /**
  82. * fetch-error.js
  83. *
  84. * FetchError interface for operational errors
  85. */
  86. /**
  87. * Create FetchError instance
  88. *
  89. * @param String message Error message for human
  90. * @param String type Error type for machine
  91. * @param String systemError For Node.js system error
  92. * @return FetchError
  93. */
  94. function FetchError(message, type, systemError) {
  95. Error.call(this, message);
  96. this.message = message;
  97. this.type = type;
  98. // when err.type is `system`, err.code contains system error code
  99. if (systemError) {
  100. this.code = this.errno = systemError.code;
  101. }
  102. // hide custom error implementation details from end-users
  103. Error.captureStackTrace(this, this.constructor);
  104. }
  105. FetchError.prototype = Object.create(Error.prototype);
  106. FetchError.prototype.constructor = FetchError;
  107. FetchError.prototype.name = 'FetchError';
  108. /**
  109. * body.js
  110. *
  111. * Body interface provides common methods for Request and Response
  112. */
  113. const Stream = require('stream');
  114. var _require = require('stream');
  115. const PassThrough = _require.PassThrough;
  116. let convert;
  117. try {
  118. convert = require('encoding').convert;
  119. } catch (e) {}
  120. const INTERNALS = Symbol('Body internals');
  121. /**
  122. * Body mixin
  123. *
  124. * Ref: https://fetch.spec.whatwg.org/#body
  125. *
  126. * @param Stream body Readable stream
  127. * @param Object opts Response options
  128. * @return Void
  129. */
  130. function Body(body) {
  131. var _this = this;
  132. var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
  133. _ref$size = _ref.size;
  134. let size = _ref$size === undefined ? 0 : _ref$size;
  135. var _ref$timeout = _ref.timeout;
  136. let timeout = _ref$timeout === undefined ? 0 : _ref$timeout;
  137. if (body == null) {
  138. // body is undefined or null
  139. body = null;
  140. } else if (typeof body === 'string') {
  141. // body is string
  142. } else if (isURLSearchParams(body)) {
  143. // body is a URLSearchParams
  144. } else if (body instanceof Blob) {
  145. // body is blob
  146. } else if (Buffer.isBuffer(body)) {
  147. // body is buffer
  148. } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
  149. // body is array buffer
  150. } else if (body instanceof Stream) {
  151. // body is stream
  152. } else {
  153. // none of the above
  154. // coerce to string
  155. body = String(body);
  156. }
  157. this[INTERNALS] = {
  158. body,
  159. disturbed: false,
  160. error: null
  161. };
  162. this.size = size;
  163. this.timeout = timeout;
  164. if (body instanceof Stream) {
  165. body.on('error', function (err) {
  166. _this[INTERNALS].error = new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err);
  167. });
  168. }
  169. }
  170. Body.prototype = {
  171. get body() {
  172. return this[INTERNALS].body;
  173. },
  174. get bodyUsed() {
  175. return this[INTERNALS].disturbed;
  176. },
  177. /**
  178. * Decode response as ArrayBuffer
  179. *
  180. * @return Promise
  181. */
  182. arrayBuffer() {
  183. return consumeBody.call(this).then(function (buf) {
  184. return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
  185. });
  186. },
  187. /**
  188. * Return raw response as Blob
  189. *
  190. * @return Promise
  191. */
  192. blob() {
  193. let ct = this.headers && this.headers.get('content-type') || '';
  194. return consumeBody.call(this).then(function (buf) {
  195. return Object.assign(
  196. // Prevent copying
  197. new Blob([], {
  198. type: ct.toLowerCase()
  199. }), {
  200. [BUFFER]: buf
  201. });
  202. });
  203. },
  204. /**
  205. * Decode response as json
  206. *
  207. * @return Promise
  208. */
  209. json() {
  210. var _this2 = this;
  211. return consumeBody.call(this).then(function (buffer) {
  212. try {
  213. return JSON.parse(buffer.toString());
  214. } catch (err) {
  215. return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json'));
  216. }
  217. });
  218. },
  219. /**
  220. * Decode response as text
  221. *
  222. * @return Promise
  223. */
  224. text() {
  225. return consumeBody.call(this).then(function (buffer) {
  226. return buffer.toString();
  227. });
  228. },
  229. /**
  230. * Decode response as buffer (non-spec api)
  231. *
  232. * @return Promise
  233. */
  234. buffer() {
  235. return consumeBody.call(this);
  236. },
  237. /**
  238. * Decode response as text, while automatically detecting the encoding and
  239. * trying to decode to UTF-8 (non-spec api)
  240. *
  241. * @return Promise
  242. */
  243. textConverted() {
  244. var _this3 = this;
  245. return consumeBody.call(this).then(function (buffer) {
  246. return convertBody(buffer, _this3.headers);
  247. });
  248. }
  249. };
  250. // In browsers, all properties are enumerable.
  251. Object.defineProperties(Body.prototype, {
  252. body: { enumerable: true },
  253. bodyUsed: { enumerable: true },
  254. arrayBuffer: { enumerable: true },
  255. blob: { enumerable: true },
  256. json: { enumerable: true },
  257. text: { enumerable: true }
  258. });
  259. Body.mixIn = function (proto) {
  260. for (const name of Object.getOwnPropertyNames(Body.prototype)) {
  261. // istanbul ignore else: future proof
  262. if (!(name in proto)) {
  263. const desc = Object.getOwnPropertyDescriptor(Body.prototype, name);
  264. Object.defineProperty(proto, name, desc);
  265. }
  266. }
  267. };
  268. /**
  269. * Consume and convert an entire Body to a Buffer.
  270. *
  271. * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body
  272. *
  273. * @return Promise
  274. */
  275. function consumeBody() {
  276. var _this4 = this;
  277. if (this[INTERNALS].disturbed) {
  278. return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`));
  279. }
  280. this[INTERNALS].disturbed = true;
  281. if (this[INTERNALS].error) {
  282. return Body.Promise.reject(this[INTERNALS].error);
  283. }
  284. // body is null
  285. if (this.body === null) {
  286. return Body.Promise.resolve(Buffer.alloc(0));
  287. }
  288. // body is string
  289. if (typeof this.body === 'string') {
  290. return Body.Promise.resolve(Buffer.from(this.body));
  291. }
  292. // body is blob
  293. if (this.body instanceof Blob) {
  294. return Body.Promise.resolve(this.body[BUFFER]);
  295. }
  296. // body is buffer
  297. if (Buffer.isBuffer(this.body)) {
  298. return Body.Promise.resolve(this.body);
  299. }
  300. // body is buffer
  301. if (Object.prototype.toString.call(this.body) === '[object ArrayBuffer]') {
  302. return Body.Promise.resolve(Buffer.from(this.body));
  303. }
  304. // istanbul ignore if: should never happen
  305. if (!(this.body instanceof Stream)) {
  306. return Body.Promise.resolve(Buffer.alloc(0));
  307. }
  308. // body is stream
  309. // get ready to actually consume the body
  310. let accum = [];
  311. let accumBytes = 0;
  312. let abort = false;
  313. return new Body.Promise(function (resolve, reject) {
  314. let resTimeout;
  315. // allow timeout on slow response body
  316. if (_this4.timeout) {
  317. resTimeout = setTimeout(function () {
  318. abort = true;
  319. reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout'));
  320. }, _this4.timeout);
  321. }
  322. // handle stream error, such as incorrect content-encoding
  323. _this4.body.on('error', function (err) {
  324. reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err));
  325. });
  326. _this4.body.on('data', function (chunk) {
  327. if (abort || chunk === null) {
  328. return;
  329. }
  330. if (_this4.size && accumBytes + chunk.length > _this4.size) {
  331. abort = true;
  332. reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size'));
  333. return;
  334. }
  335. accumBytes += chunk.length;
  336. accum.push(chunk);
  337. });
  338. _this4.body.on('end', function () {
  339. if (abort) {
  340. return;
  341. }
  342. clearTimeout(resTimeout);
  343. try {
  344. resolve(Buffer.concat(accum));
  345. } catch (err) {
  346. // handle streams that have accumulated too much data (issue #414)
  347. reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err));
  348. }
  349. });
  350. });
  351. }
  352. /**
  353. * Detect buffer encoding and convert to target encoding
  354. * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding
  355. *
  356. * @param Buffer buffer Incoming buffer
  357. * @param String encoding Target encoding
  358. * @return String
  359. */
  360. function convertBody(buffer, headers) {
  361. if (typeof convert !== 'function') {
  362. throw new Error('The package `encoding` must be installed to use the textConverted() function');
  363. }
  364. const ct = headers.get('content-type');
  365. let charset = 'utf-8';
  366. let res, str;
  367. // header
  368. if (ct) {
  369. res = /charset=([^;]*)/i.exec(ct);
  370. }
  371. // no charset in content type, peek at response body for at most 1024 bytes
  372. str = buffer.slice(0, 1024).toString();
  373. // html5
  374. if (!res && str) {
  375. res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str);
  376. }
  377. // html4
  378. if (!res && str) {
  379. res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str);
  380. if (res) {
  381. res = /charset=(.*)/i.exec(res.pop());
  382. }
  383. }
  384. // xml
  385. if (!res && str) {
  386. res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str);
  387. }
  388. // found charset
  389. if (res) {
  390. charset = res.pop();
  391. // prevent decode issues when sites use incorrect encoding
  392. // ref: https://hsivonen.fi/encoding-menu/
  393. if (charset === 'gb2312' || charset === 'gbk') {
  394. charset = 'gb18030';
  395. }
  396. }
  397. // turn raw buffers into a single utf-8 buffer
  398. return convert(buffer, 'UTF-8', charset).toString();
  399. }
  400. /**
  401. * Detect a URLSearchParams object
  402. * ref: https://github.com/bitinn/node-fetch/issues/296#issuecomment-307598143
  403. *
  404. * @param Object obj Object to detect by type or brand
  405. * @return String
  406. */
  407. function isURLSearchParams(obj) {
  408. // Duck-typing as a necessary condition.
  409. if (typeof obj !== 'object' || typeof obj.append !== 'function' || typeof obj.delete !== 'function' || typeof obj.get !== 'function' || typeof obj.getAll !== 'function' || typeof obj.has !== 'function' || typeof obj.set !== 'function') {
  410. return false;
  411. }
  412. // Brand-checking and more duck-typing as optional condition.
  413. return obj.constructor.name === 'URLSearchParams' || Object.prototype.toString.call(obj) === '[object URLSearchParams]' || typeof obj.sort === 'function';
  414. }
  415. /**
  416. * Clone body given Res/Req instance
  417. *
  418. * @param Mixed instance Response or Request instance
  419. * @return Mixed
  420. */
  421. function clone(instance) {
  422. let p1, p2;
  423. let body = instance.body;
  424. // don't allow cloning a used body
  425. if (instance.bodyUsed) {
  426. throw new Error('cannot clone body after it is used');
  427. }
  428. // check that body is a stream and not form-data object
  429. // note: we can't clone the form-data object without having it as a dependency
  430. if (body instanceof Stream && typeof body.getBoundary !== 'function') {
  431. // tee instance body
  432. p1 = new PassThrough();
  433. p2 = new PassThrough();
  434. body.pipe(p1);
  435. body.pipe(p2);
  436. // set instance body to teed body and return the other teed body
  437. instance[INTERNALS].body = p1;
  438. body = p2;
  439. }
  440. return body;
  441. }
  442. /**
  443. * Performs the operation "extract a `Content-Type` value from |object|" as
  444. * specified in the specification:
  445. * https://fetch.spec.whatwg.org/#concept-bodyinit-extract
  446. *
  447. * This function assumes that instance.body is present.
  448. *
  449. * @param Mixed instance Response or Request instance
  450. */
  451. function extractContentType(instance) {
  452. const body = instance.body;
  453. // istanbul ignore if: Currently, because of a guard in Request, body
  454. // can never be null. Included here for completeness.
  455. if (body === null) {
  456. // body is null
  457. return null;
  458. } else if (typeof body === 'string') {
  459. // body is string
  460. return 'text/plain;charset=UTF-8';
  461. } else if (isURLSearchParams(body)) {
  462. // body is a URLSearchParams
  463. return 'application/x-www-form-urlencoded;charset=UTF-8';
  464. } else if (body instanceof Blob) {
  465. // body is blob
  466. return body.type || null;
  467. } else if (Buffer.isBuffer(body)) {
  468. // body is buffer
  469. return null;
  470. } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
  471. // body is array buffer
  472. return null;
  473. } else if (typeof body.getBoundary === 'function') {
  474. // detect form data input from form-data module
  475. return `multipart/form-data;boundary=${body.getBoundary()}`;
  476. } else {
  477. // body is stream
  478. // can't really do much about this
  479. return null;
  480. }
  481. }
  482. /**
  483. * The Fetch Standard treats this as if "total bytes" is a property on the body.
  484. * For us, we have to explicitly get it with a function.
  485. *
  486. * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes
  487. *
  488. * @param Body instance Instance of Body
  489. * @return Number? Number of bytes, or null if not possible
  490. */
  491. function getTotalBytes(instance) {
  492. const body = instance.body;
  493. // istanbul ignore if: included for completion
  494. if (body === null) {
  495. // body is null
  496. return 0;
  497. } else if (typeof body === 'string') {
  498. // body is string
  499. return Buffer.byteLength(body);
  500. } else if (isURLSearchParams(body)) {
  501. // body is URLSearchParams
  502. return Buffer.byteLength(String(body));
  503. } else if (body instanceof Blob) {
  504. // body is blob
  505. return body.size;
  506. } else if (Buffer.isBuffer(body)) {
  507. // body is buffer
  508. return body.length;
  509. } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
  510. // body is array buffer
  511. return body.byteLength;
  512. } else if (body && typeof body.getLengthSync === 'function') {
  513. // detect form data input from form-data module
  514. if (body._lengthRetrievers && body._lengthRetrievers.length == 0 || // 1.x
  515. body.hasKnownLength && body.hasKnownLength()) {
  516. // 2.x
  517. return body.getLengthSync();
  518. }
  519. return null;
  520. } else {
  521. // body is stream
  522. // can't really do much about this
  523. return null;
  524. }
  525. }
  526. /**
  527. * Write a Body to a Node.js WritableStream (e.g. http.Request) object.
  528. *
  529. * @param Body instance Instance of Body
  530. * @return Void
  531. */
  532. function writeToStream(dest, instance) {
  533. const body = instance.body;
  534. if (body === null) {
  535. // body is null
  536. dest.end();
  537. } else if (typeof body === 'string') {
  538. // body is string
  539. dest.write(body);
  540. dest.end();
  541. } else if (isURLSearchParams(body)) {
  542. // body is URLSearchParams
  543. dest.write(Buffer.from(String(body)));
  544. dest.end();
  545. } else if (body instanceof Blob) {
  546. // body is blob
  547. dest.write(body[BUFFER]);
  548. dest.end();
  549. } else if (Buffer.isBuffer(body)) {
  550. // body is buffer
  551. dest.write(body);
  552. dest.end();
  553. } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
  554. // body is array buffer
  555. dest.write(Buffer.from(body));
  556. dest.end();
  557. } else {
  558. // body is stream
  559. body.pipe(dest);
  560. }
  561. }
  562. // expose Promise
  563. Body.Promise = global.Promise;
  564. /**
  565. * headers.js
  566. *
  567. * Headers class offers convenient helpers
  568. */
  569. const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/;
  570. const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
  571. function validateName(name) {
  572. name = `${name}`;
  573. if (invalidTokenRegex.test(name)) {
  574. throw new TypeError(`${name} is not a legal HTTP header name`);
  575. }
  576. }
  577. function validateValue(value) {
  578. value = `${value}`;
  579. if (invalidHeaderCharRegex.test(value)) {
  580. throw new TypeError(`${value} is not a legal HTTP header value`);
  581. }
  582. }
  583. /**
  584. * Find the key in the map object given a header name.
  585. *
  586. * Returns undefined if not found.
  587. *
  588. * @param String name Header name
  589. * @return String|Undefined
  590. */
  591. function find(map, name) {
  592. name = name.toLowerCase();
  593. for (const key in map) {
  594. if (key.toLowerCase() === name) {
  595. return key;
  596. }
  597. }
  598. return undefined;
  599. }
  600. const MAP = Symbol('map');
  601. class Headers {
  602. /**
  603. * Headers class
  604. *
  605. * @param Object headers Response headers
  606. * @return Void
  607. */
  608. constructor() {
  609. let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
  610. this[MAP] = Object.create(null);
  611. if (init instanceof Headers) {
  612. const rawHeaders = init.raw();
  613. const headerNames = Object.keys(rawHeaders);
  614. for (const headerName of headerNames) {
  615. for (const value of rawHeaders[headerName]) {
  616. this.append(headerName, value);
  617. }
  618. }
  619. return;
  620. }
  621. // We don't worry about converting prop to ByteString here as append()
  622. // will handle it.
  623. if (init == null) {
  624. // no op
  625. } else if (typeof init === 'object') {
  626. const method = init[Symbol.iterator];
  627. if (method != null) {
  628. if (typeof method !== 'function') {
  629. throw new TypeError('Header pairs must be iterable');
  630. }
  631. // sequence<sequence<ByteString>>
  632. // Note: per spec we have to first exhaust the lists then process them
  633. const pairs = [];
  634. for (const pair of init) {
  635. if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') {
  636. throw new TypeError('Each header pair must be iterable');
  637. }
  638. pairs.push(Array.from(pair));
  639. }
  640. for (const pair of pairs) {
  641. if (pair.length !== 2) {
  642. throw new TypeError('Each header pair must be a name/value tuple');
  643. }
  644. this.append(pair[0], pair[1]);
  645. }
  646. } else {
  647. // record<ByteString, ByteString>
  648. for (const key of Object.keys(init)) {
  649. const value = init[key];
  650. this.append(key, value);
  651. }
  652. }
  653. } else {
  654. throw new TypeError('Provided initializer must be an object');
  655. }
  656. }
  657. /**
  658. * Return combined header value given name
  659. *
  660. * @param String name Header name
  661. * @return Mixed
  662. */
  663. get(name) {
  664. name = `${name}`;
  665. validateName(name);
  666. const key = find(this[MAP], name);
  667. if (key === undefined) {
  668. return null;
  669. }
  670. return this[MAP][key].join(', ');
  671. }
  672. /**
  673. * Iterate over all headers
  674. *
  675. * @param Function callback Executed for each item with parameters (value, name, thisArg)
  676. * @param Boolean thisArg `this` context for callback function
  677. * @return Void
  678. */
  679. forEach(callback) {
  680. let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
  681. let pairs = getHeaders(this);
  682. let i = 0;
  683. while (i < pairs.length) {
  684. var _pairs$i = pairs[i];
  685. const name = _pairs$i[0],
  686. value = _pairs$i[1];
  687. callback.call(thisArg, value, name, this);
  688. pairs = getHeaders(this);
  689. i++;
  690. }
  691. }
  692. /**
  693. * Overwrite header values given name
  694. *
  695. * @param String name Header name
  696. * @param String value Header value
  697. * @return Void
  698. */
  699. set(name, value) {
  700. name = `${name}`;
  701. value = `${value}`;
  702. validateName(name);
  703. validateValue(value);
  704. const key = find(this[MAP], name);
  705. this[MAP][key !== undefined ? key : name] = [value];
  706. }
  707. /**
  708. * Append a value onto existing header
  709. *
  710. * @param String name Header name
  711. * @param String value Header value
  712. * @return Void
  713. */
  714. append(name, value) {
  715. name = `${name}`;
  716. value = `${value}`;
  717. validateName(name);
  718. validateValue(value);
  719. const key = find(this[MAP], name);
  720. if (key !== undefined) {
  721. this[MAP][key].push(value);
  722. } else {
  723. this[MAP][name] = [value];
  724. }
  725. }
  726. /**
  727. * Check for header name existence
  728. *
  729. * @param String name Header name
  730. * @return Boolean
  731. */
  732. has(name) {
  733. name = `${name}`;
  734. validateName(name);
  735. return find(this[MAP], name) !== undefined;
  736. }
  737. /**
  738. * Delete all header values given name
  739. *
  740. * @param String name Header name
  741. * @return Void
  742. */
  743. delete(name) {
  744. name = `${name}`;
  745. validateName(name);
  746. const key = find(this[MAP], name);
  747. if (key !== undefined) {
  748. delete this[MAP][key];
  749. }
  750. }
  751. /**
  752. * Return raw headers (non-spec api)
  753. *
  754. * @return Object
  755. */
  756. raw() {
  757. return this[MAP];
  758. }
  759. /**
  760. * Get an iterator on keys.
  761. *
  762. * @return Iterator
  763. */
  764. keys() {
  765. return createHeadersIterator(this, 'key');
  766. }
  767. /**
  768. * Get an iterator on values.
  769. *
  770. * @return Iterator
  771. */
  772. values() {
  773. return createHeadersIterator(this, 'value');
  774. }
  775. /**
  776. * Get an iterator on entries.
  777. *
  778. * This is the default iterator of the Headers object.
  779. *
  780. * @return Iterator
  781. */
  782. [Symbol.iterator]() {
  783. return createHeadersIterator(this, 'key+value');
  784. }
  785. }
  786. Headers.prototype.entries = Headers.prototype[Symbol.iterator];
  787. Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
  788. value: 'Headers',
  789. writable: false,
  790. enumerable: false,
  791. configurable: true
  792. });
  793. Object.defineProperties(Headers.prototype, {
  794. get: { enumerable: true },
  795. forEach: { enumerable: true },
  796. set: { enumerable: true },
  797. append: { enumerable: true },
  798. has: { enumerable: true },
  799. delete: { enumerable: true },
  800. keys: { enumerable: true },
  801. values: { enumerable: true },
  802. entries: { enumerable: true }
  803. });
  804. function getHeaders(headers) {
  805. let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value';
  806. const keys = Object.keys(headers[MAP]).sort();
  807. return keys.map(kind === 'key' ? function (k) {
  808. return k.toLowerCase();
  809. } : kind === 'value' ? function (k) {
  810. return headers[MAP][k].join(', ');
  811. } : function (k) {
  812. return [k.toLowerCase(), headers[MAP][k].join(', ')];
  813. });
  814. }
  815. const INTERNAL = Symbol('internal');
  816. function createHeadersIterator(target, kind) {
  817. const iterator = Object.create(HeadersIteratorPrototype);
  818. iterator[INTERNAL] = {
  819. target,
  820. kind,
  821. index: 0
  822. };
  823. return iterator;
  824. }
  825. const HeadersIteratorPrototype = Object.setPrototypeOf({
  826. next() {
  827. // istanbul ignore if
  828. if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
  829. throw new TypeError('Value of `this` is not a HeadersIterator');
  830. }
  831. var _INTERNAL = this[INTERNAL];
  832. const target = _INTERNAL.target,
  833. kind = _INTERNAL.kind,
  834. index = _INTERNAL.index;
  835. const values = getHeaders(target, kind);
  836. const len = values.length;
  837. if (index >= len) {
  838. return {
  839. value: undefined,
  840. done: true
  841. };
  842. }
  843. this[INTERNAL].index = index + 1;
  844. return {
  845. value: values[index],
  846. done: false
  847. };
  848. }
  849. }, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
  850. Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, {
  851. value: 'HeadersIterator',
  852. writable: false,
  853. enumerable: false,
  854. configurable: true
  855. });
  856. /**
  857. * Export the Headers object in a form that Node.js can consume.
  858. *
  859. * @param Headers headers
  860. * @return Object
  861. */
  862. function exportNodeCompatibleHeaders(headers) {
  863. const obj = Object.assign({ __proto__: null }, headers[MAP]);
  864. // http.request() only supports string as Host header. This hack makes
  865. // specifying custom Host header possible.
  866. const hostHeaderKey = find(headers[MAP], 'Host');
  867. if (hostHeaderKey !== undefined) {
  868. obj[hostHeaderKey] = obj[hostHeaderKey][0];
  869. }
  870. return obj;
  871. }
  872. /**
  873. * Create a Headers object from an object of headers, ignoring those that do
  874. * not conform to HTTP grammar productions.
  875. *
  876. * @param Object obj Object of headers
  877. * @return Headers
  878. */
  879. function createHeadersLenient(obj) {
  880. const headers = new Headers();
  881. for (const name of Object.keys(obj)) {
  882. if (invalidTokenRegex.test(name)) {
  883. continue;
  884. }
  885. if (Array.isArray(obj[name])) {
  886. for (const val of obj[name]) {
  887. if (invalidHeaderCharRegex.test(val)) {
  888. continue;
  889. }
  890. if (headers[MAP][name] === undefined) {
  891. headers[MAP][name] = [val];
  892. } else {
  893. headers[MAP][name].push(val);
  894. }
  895. }
  896. } else if (!invalidHeaderCharRegex.test(obj[name])) {
  897. headers[MAP][name] = [obj[name]];
  898. }
  899. }
  900. return headers;
  901. }
  902. /**
  903. * response.js
  904. *
  905. * Response class provides content decoding
  906. */
  907. var _require$1 = require('http');
  908. const STATUS_CODES = _require$1.STATUS_CODES;
  909. const INTERNALS$1 = Symbol('Response internals');
  910. /**
  911. * Response class
  912. *
  913. * @param Stream body Readable stream
  914. * @param Object opts Response options
  915. * @return Void
  916. */
  917. class Response {
  918. constructor() {
  919. let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
  920. let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  921. Body.call(this, body, opts);
  922. const status = opts.status || 200;
  923. this[INTERNALS$1] = {
  924. url: opts.url,
  925. status,
  926. statusText: opts.statusText || STATUS_CODES[status],
  927. headers: new Headers(opts.headers)
  928. };
  929. }
  930. get url() {
  931. return this[INTERNALS$1].url;
  932. }
  933. get status() {
  934. return this[INTERNALS$1].status;
  935. }
  936. /**
  937. * Convenience property representing if the request ended normally
  938. */
  939. get ok() {
  940. return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300;
  941. }
  942. get statusText() {
  943. return this[INTERNALS$1].statusText;
  944. }
  945. get headers() {
  946. return this[INTERNALS$1].headers;
  947. }
  948. /**
  949. * Clone this response
  950. *
  951. * @return Response
  952. */
  953. clone() {
  954. return new Response(clone(this), {
  955. url: this.url,
  956. status: this.status,
  957. statusText: this.statusText,
  958. headers: this.headers,
  959. ok: this.ok
  960. });
  961. }
  962. }
  963. Body.mixIn(Response.prototype);
  964. Object.defineProperties(Response.prototype, {
  965. url: { enumerable: true },
  966. status: { enumerable: true },
  967. ok: { enumerable: true },
  968. statusText: { enumerable: true },
  969. headers: { enumerable: true },
  970. clone: { enumerable: true }
  971. });
  972. Object.defineProperty(Response.prototype, Symbol.toStringTag, {
  973. value: 'Response',
  974. writable: false,
  975. enumerable: false,
  976. configurable: true
  977. });
  978. /**
  979. * request.js
  980. *
  981. * Request class contains server only options
  982. *
  983. * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
  984. */
  985. var _require$2 = require('url');
  986. const format_url = _require$2.format;
  987. const parse_url = _require$2.parse;
  988. const INTERNALS$2 = Symbol('Request internals');
  989. /**
  990. * Check if a value is an instance of Request.
  991. *
  992. * @param Mixed input
  993. * @return Boolean
  994. */
  995. function isRequest(input) {
  996. return typeof input === 'object' && typeof input[INTERNALS$2] === 'object';
  997. }
  998. /**
  999. * Request class
  1000. *
  1001. * @param Mixed input Url or Request instance
  1002. * @param Object init Custom options
  1003. * @return Void
  1004. */
  1005. class Request {
  1006. constructor(input) {
  1007. let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  1008. let parsedURL;
  1009. // normalize input
  1010. if (!isRequest(input)) {
  1011. if (input && input.href) {
  1012. // in order to support Node.js' Url objects; though WHATWG's URL objects
  1013. // will fall into this branch also (since their `toString()` will return
  1014. // `href` property anyway)
  1015. parsedURL = parse_url(input.href);
  1016. } else {
  1017. // coerce input to a string before attempting to parse
  1018. parsedURL = parse_url(`${input}`);
  1019. }
  1020. input = {};
  1021. } else {
  1022. parsedURL = parse_url(input.url);
  1023. }
  1024. let method = init.method || input.method || 'GET';
  1025. method = method.toUpperCase();
  1026. if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) {
  1027. throw new TypeError('Request with GET/HEAD method cannot have body');
  1028. }
  1029. let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null;
  1030. Body.call(this, inputBody, {
  1031. timeout: init.timeout || input.timeout || 0,
  1032. size: init.size || input.size || 0
  1033. });
  1034. const headers = new Headers(init.headers || input.headers || {});
  1035. if (init.body != null) {
  1036. const contentType = extractContentType(this);
  1037. if (contentType !== null && !headers.has('Content-Type')) {
  1038. headers.append('Content-Type', contentType);
  1039. }
  1040. }
  1041. this[INTERNALS$2] = {
  1042. method,
  1043. redirect: init.redirect || input.redirect || 'follow',
  1044. headers,
  1045. parsedURL
  1046. };
  1047. // node-fetch-only options
  1048. this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20;
  1049. this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true;
  1050. this.counter = init.counter || input.counter || 0;
  1051. this.agent = init.agent || input.agent;
  1052. }
  1053. get method() {
  1054. return this[INTERNALS$2].method;
  1055. }
  1056. get url() {
  1057. return format_url(this[INTERNALS$2].parsedURL);
  1058. }
  1059. get headers() {
  1060. return this[INTERNALS$2].headers;
  1061. }
  1062. get redirect() {
  1063. return this[INTERNALS$2].redirect;
  1064. }
  1065. /**
  1066. * Clone this request
  1067. *
  1068. * @return Request
  1069. */
  1070. clone() {
  1071. return new Request(this);
  1072. }
  1073. }
  1074. Body.mixIn(Request.prototype);
  1075. Object.defineProperty(Request.prototype, Symbol.toStringTag, {
  1076. value: 'Request',
  1077. writable: false,
  1078. enumerable: false,
  1079. configurable: true
  1080. });
  1081. Object.defineProperties(Request.prototype, {
  1082. method: { enumerable: true },
  1083. url: { enumerable: true },
  1084. headers: { enumerable: true },
  1085. redirect: { enumerable: true },
  1086. clone: { enumerable: true }
  1087. });
  1088. /**
  1089. * Convert a Request to Node.js http request options.
  1090. *
  1091. * @param Request A Request instance
  1092. * @return Object The options object to be passed to http.request
  1093. */
  1094. function getNodeRequestOptions(request) {
  1095. const parsedURL = request[INTERNALS$2].parsedURL;
  1096. const headers = new Headers(request[INTERNALS$2].headers);
  1097. // fetch step 1.3
  1098. if (!headers.has('Accept')) {
  1099. headers.set('Accept', '*/*');
  1100. }
  1101. // Basic fetch
  1102. if (!parsedURL.protocol || !parsedURL.hostname) {
  1103. throw new TypeError('Only absolute URLs are supported');
  1104. }
  1105. if (!/^https?:$/.test(parsedURL.protocol)) {
  1106. throw new TypeError('Only HTTP(S) protocols are supported');
  1107. }
  1108. // HTTP-network-or-cache fetch steps 2.4-2.7
  1109. let contentLengthValue = null;
  1110. if (request.body == null && /^(POST|PUT)$/i.test(request.method)) {
  1111. contentLengthValue = '0';
  1112. }
  1113. if (request.body != null) {
  1114. const totalBytes = getTotalBytes(request);
  1115. if (typeof totalBytes === 'number') {
  1116. contentLengthValue = String(totalBytes);
  1117. }
  1118. }
  1119. if (contentLengthValue) {
  1120. headers.set('Content-Length', contentLengthValue);
  1121. }
  1122. // HTTP-network-or-cache fetch step 2.11
  1123. if (!headers.has('User-Agent')) {
  1124. headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)');
  1125. }
  1126. // HTTP-network-or-cache fetch step 2.15
  1127. if (request.compress) {
  1128. headers.set('Accept-Encoding', 'gzip,deflate');
  1129. }
  1130. if (!headers.has('Connection') && !request.agent) {
  1131. headers.set('Connection', 'close');
  1132. }
  1133. // HTTP-network fetch step 4.2
  1134. // chunked encoding is handled by Node.js
  1135. return Object.assign({}, parsedURL, {
  1136. method: request.method,
  1137. headers: exportNodeCompatibleHeaders(headers),
  1138. agent: request.agent
  1139. });
  1140. }
  1141. /**
  1142. * index.js
  1143. *
  1144. * a request API compatible with window.fetch
  1145. *
  1146. * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
  1147. */
  1148. const http = require('http');
  1149. const https = require('https');
  1150. var _require$3 = require('stream');
  1151. const PassThrough$1 = _require$3.PassThrough;
  1152. var _require2 = require('url');
  1153. const resolve_url = _require2.resolve;
  1154. const zlib = require('zlib');
  1155. /**
  1156. * Fetch function
  1157. *
  1158. * @param Mixed url Absolute url or Request instance
  1159. * @param Object opts Fetch options
  1160. * @return Promise
  1161. */
  1162. function fetch(url, opts) {
  1163. // allow custom promise
  1164. if (!fetch.Promise) {
  1165. throw new Error('native promise missing, set fetch.Promise to your favorite alternative');
  1166. }
  1167. Body.Promise = fetch.Promise;
  1168. // wrap http.request into fetch
  1169. return new fetch.Promise(function (resolve, reject) {
  1170. // build request object
  1171. const request = new Request(url, opts);
  1172. const options = getNodeRequestOptions(request);
  1173. const send = (options.protocol === 'https:' ? https : http).request;
  1174. // send request
  1175. const req = send(options);
  1176. let reqTimeout;
  1177. function finalize() {
  1178. req.abort();
  1179. clearTimeout(reqTimeout);
  1180. }
  1181. if (request.timeout) {
  1182. req.once('socket', function (socket) {
  1183. reqTimeout = setTimeout(function () {
  1184. reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout'));
  1185. finalize();
  1186. }, request.timeout);
  1187. });
  1188. }
  1189. req.on('error', function (err) {
  1190. reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
  1191. finalize();
  1192. });
  1193. req.on('response', function (res) {
  1194. clearTimeout(reqTimeout);
  1195. const headers = createHeadersLenient(res.headers);
  1196. // HTTP fetch step 5
  1197. if (fetch.isRedirect(res.statusCode)) {
  1198. // HTTP fetch step 5.2
  1199. const location = headers.get('Location');
  1200. // HTTP fetch step 5.3
  1201. const locationURL = location === null ? null : resolve_url(request.url, location);
  1202. // HTTP fetch step 5.5
  1203. switch (request.redirect) {
  1204. case 'error':
  1205. reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect'));
  1206. finalize();
  1207. return;
  1208. case 'manual':
  1209. // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL.
  1210. if (locationURL !== null) {
  1211. headers.set('Location', locationURL);
  1212. }
  1213. break;
  1214. case 'follow':
  1215. // HTTP-redirect fetch step 2
  1216. if (locationURL === null) {
  1217. break;
  1218. }
  1219. // HTTP-redirect fetch step 5
  1220. if (request.counter >= request.follow) {
  1221. reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect'));
  1222. finalize();
  1223. return;
  1224. }
  1225. // HTTP-redirect fetch step 6 (counter increment)
  1226. // Create a new Request object.
  1227. const requestOpts = {
  1228. headers: new Headers(request.headers),
  1229. follow: request.follow,
  1230. counter: request.counter + 1,
  1231. agent: request.agent,
  1232. compress: request.compress,
  1233. method: request.method,
  1234. body: request.body
  1235. };
  1236. // HTTP-redirect fetch step 9
  1237. if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) {
  1238. reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));
  1239. finalize();
  1240. return;
  1241. }
  1242. // HTTP-redirect fetch step 11
  1243. if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') {
  1244. requestOpts.method = 'GET';
  1245. requestOpts.body = undefined;
  1246. requestOpts.headers.delete('content-length');
  1247. }
  1248. // HTTP-redirect fetch step 15
  1249. resolve(fetch(new Request(locationURL, requestOpts)));
  1250. finalize();
  1251. return;
  1252. }
  1253. }
  1254. // prepare response
  1255. let body = res.pipe(new PassThrough$1());
  1256. const response_options = {
  1257. url: request.url,
  1258. status: res.statusCode,
  1259. statusText: res.statusMessage,
  1260. headers: headers,
  1261. size: request.size,
  1262. timeout: request.timeout
  1263. };
  1264. // HTTP-network fetch step 12.1.1.3
  1265. const codings = headers.get('Content-Encoding');
  1266. // HTTP-network fetch step 12.1.1.4: handle content codings
  1267. // in following scenarios we ignore compression support
  1268. // 1. compression support is disabled
  1269. // 2. HEAD request
  1270. // 3. no Content-Encoding header
  1271. // 4. no content response (204)
  1272. // 5. content not modified response (304)
  1273. if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) {
  1274. resolve(new Response(body, response_options));
  1275. return;
  1276. }
  1277. // For Node v6+
  1278. // Be less strict when decoding compressed responses, since sometimes
  1279. // servers send slightly invalid responses that are still accepted
  1280. // by common browsers.
  1281. // Always using Z_SYNC_FLUSH is what cURL does.
  1282. const zlibOptions = {
  1283. flush: zlib.Z_SYNC_FLUSH,
  1284. finishFlush: zlib.Z_SYNC_FLUSH
  1285. };
  1286. // for gzip
  1287. if (codings == 'gzip' || codings == 'x-gzip') {
  1288. body = body.pipe(zlib.createGunzip(zlibOptions));
  1289. resolve(new Response(body, response_options));
  1290. return;
  1291. }
  1292. // for deflate
  1293. if (codings == 'deflate' || codings == 'x-deflate') {
  1294. // handle the infamous raw deflate response from old servers
  1295. // a hack for old IIS and Apache servers
  1296. const raw = res.pipe(new PassThrough$1());
  1297. raw.once('data', function (chunk) {
  1298. // see http://stackoverflow.com/questions/37519828
  1299. if ((chunk[0] & 0x0F) === 0x08) {
  1300. body = body.pipe(zlib.createInflate());
  1301. } else {
  1302. body = body.pipe(zlib.createInflateRaw());
  1303. }
  1304. resolve(new Response(body, response_options));
  1305. });
  1306. return;
  1307. }
  1308. // otherwise, use response as-is
  1309. resolve(new Response(body, response_options));
  1310. });
  1311. writeToStream(req, request);
  1312. });
  1313. }
  1314. /**
  1315. * Redirect code matching
  1316. *
  1317. * @param Number code Status code
  1318. * @return Boolean
  1319. */
  1320. fetch.isRedirect = function (code) {
  1321. return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
  1322. };
  1323. // Needed for TypeScript.
  1324. fetch.default = fetch;
  1325. // expose Promise
  1326. fetch.Promise = global.Promise;
  1327. export default fetch;
  1328. export { Headers, Request, Response, FetchError };