fetch.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /* eslint-disable no-prototype-builtins */
  2. var g =
  3. (typeof globalThis !== 'undefined' && globalThis) ||
  4. (typeof self !== 'undefined' && self) ||
  5. // eslint-disable-next-line no-undef
  6. (typeof global !== 'undefined' && global) ||
  7. {}
  8. var support = {
  9. searchParams: 'URLSearchParams' in g,
  10. iterable: 'Symbol' in g && 'iterator' in Symbol,
  11. blob:
  12. 'FileReader' in g &&
  13. 'Blob' in g &&
  14. (function() {
  15. try {
  16. new Blob()
  17. return true
  18. } catch (e) {
  19. return false
  20. }
  21. })(),
  22. formData: 'FormData' in g,
  23. arrayBuffer: 'ArrayBuffer' in g
  24. }
  25. function isDataView(obj) {
  26. return obj && DataView.prototype.isPrototypeOf(obj)
  27. }
  28. if (support.arrayBuffer) {
  29. var viewClasses = [
  30. '[object Int8Array]',
  31. '[object Uint8Array]',
  32. '[object Uint8ClampedArray]',
  33. '[object Int16Array]',
  34. '[object Uint16Array]',
  35. '[object Int32Array]',
  36. '[object Uint32Array]',
  37. '[object Float32Array]',
  38. '[object Float64Array]'
  39. ]
  40. var isArrayBufferView =
  41. ArrayBuffer.isView ||
  42. function(obj) {
  43. return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
  44. }
  45. }
  46. function normalizeName(name) {
  47. if (typeof name !== 'string') {
  48. name = String(name)
  49. }
  50. if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
  51. throw new TypeError('Invalid character in header field name: "' + name + '"')
  52. }
  53. return name.toLowerCase()
  54. }
  55. function normalizeValue(value) {
  56. if (typeof value !== 'string') {
  57. value = String(value)
  58. }
  59. return value
  60. }
  61. // Build a destructive iterator for the value list
  62. function iteratorFor(items) {
  63. var iterator = {
  64. next: function() {
  65. var value = items.shift()
  66. return {done: value === undefined, value: value}
  67. }
  68. }
  69. if (support.iterable) {
  70. iterator[Symbol.iterator] = function() {
  71. return iterator
  72. }
  73. }
  74. return iterator
  75. }
  76. export function Headers(headers) {
  77. this.map = {}
  78. if (headers instanceof Headers) {
  79. headers.forEach(function(value, name) {
  80. this.append(name, value)
  81. }, this)
  82. } else if (Array.isArray(headers)) {
  83. headers.forEach(function(header) {
  84. if (header.length != 2) {
  85. throw new TypeError('Headers constructor: expected name/value pair to be length 2, found' + header.length)
  86. }
  87. this.append(header[0], header[1])
  88. }, this)
  89. } else if (headers) {
  90. Object.getOwnPropertyNames(headers).forEach(function(name) {
  91. this.append(name, headers[name])
  92. }, this)
  93. }
  94. }
  95. Headers.prototype.append = function(name, value) {
  96. name = normalizeName(name)
  97. value = normalizeValue(value)
  98. var oldValue = this.map[name]
  99. this.map[name] = oldValue ? oldValue + ', ' + value : value
  100. }
  101. Headers.prototype['delete'] = function(name) {
  102. delete this.map[normalizeName(name)]
  103. }
  104. Headers.prototype.get = function(name) {
  105. name = normalizeName(name)
  106. return this.has(name) ? this.map[name] : null
  107. }
  108. Headers.prototype.has = function(name) {
  109. return this.map.hasOwnProperty(normalizeName(name))
  110. }
  111. Headers.prototype.set = function(name, value) {
  112. this.map[normalizeName(name)] = normalizeValue(value)
  113. }
  114. Headers.prototype.forEach = function(callback, thisArg) {
  115. for (var name in this.map) {
  116. if (this.map.hasOwnProperty(name)) {
  117. callback.call(thisArg, this.map[name], name, this)
  118. }
  119. }
  120. }
  121. Headers.prototype.keys = function() {
  122. var items = []
  123. this.forEach(function(value, name) {
  124. items.push(name)
  125. })
  126. return iteratorFor(items)
  127. }
  128. Headers.prototype.values = function() {
  129. var items = []
  130. this.forEach(function(value) {
  131. items.push(value)
  132. })
  133. return iteratorFor(items)
  134. }
  135. Headers.prototype.entries = function() {
  136. var items = []
  137. this.forEach(function(value, name) {
  138. items.push([name, value])
  139. })
  140. return iteratorFor(items)
  141. }
  142. if (support.iterable) {
  143. Headers.prototype[Symbol.iterator] = Headers.prototype.entries
  144. }
  145. function consumed(body) {
  146. if (body._noBody) return
  147. if (body.bodyUsed) {
  148. return Promise.reject(new TypeError('Already read'))
  149. }
  150. body.bodyUsed = true
  151. }
  152. function fileReaderReady(reader) {
  153. return new Promise(function(resolve, reject) {
  154. reader.onload = function() {
  155. resolve(reader.result)
  156. }
  157. reader.onerror = function() {
  158. reject(reader.error)
  159. }
  160. })
  161. }
  162. function readBlobAsArrayBuffer(blob) {
  163. var reader = new FileReader()
  164. var promise = fileReaderReady(reader)
  165. reader.readAsArrayBuffer(blob)
  166. return promise
  167. }
  168. function readBlobAsText(blob) {
  169. var reader = new FileReader()
  170. var promise = fileReaderReady(reader)
  171. var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type)
  172. var encoding = match ? match[1] : 'utf-8'
  173. reader.readAsText(blob, encoding)
  174. return promise
  175. }
  176. function readArrayBufferAsText(buf) {
  177. var view = new Uint8Array(buf)
  178. var chars = new Array(view.length)
  179. for (var i = 0; i < view.length; i++) {
  180. chars[i] = String.fromCharCode(view[i])
  181. }
  182. return chars.join('')
  183. }
  184. function bufferClone(buf) {
  185. if (buf.slice) {
  186. return buf.slice(0)
  187. } else {
  188. var view = new Uint8Array(buf.byteLength)
  189. view.set(new Uint8Array(buf))
  190. return view.buffer
  191. }
  192. }
  193. function Body() {
  194. this.bodyUsed = false
  195. this._initBody = function(body) {
  196. /*
  197. fetch-mock wraps the Response object in an ES6 Proxy to
  198. provide useful test harness features such as flush. However, on
  199. ES5 browsers without fetch or Proxy support pollyfills must be used;
  200. the proxy-pollyfill is unable to proxy an attribute unless it exists
  201. on the object before the Proxy is created. This change ensures
  202. Response.bodyUsed exists on the instance, while maintaining the
  203. semantic of setting Request.bodyUsed in the constructor before
  204. _initBody is called.
  205. */
  206. // eslint-disable-next-line no-self-assign
  207. this.bodyUsed = this.bodyUsed
  208. this._bodyInit = body
  209. if (!body) {
  210. this._noBody = true;
  211. this._bodyText = ''
  212. } else if (typeof body === 'string') {
  213. this._bodyText = body
  214. } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
  215. this._bodyBlob = body
  216. } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
  217. this._bodyFormData = body
  218. } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
  219. this._bodyText = body.toString()
  220. } else if (support.arrayBuffer && support.blob && isDataView(body)) {
  221. this._bodyArrayBuffer = bufferClone(body.buffer)
  222. // IE 10-11 can't handle a DataView body.
  223. this._bodyInit = new Blob([this._bodyArrayBuffer])
  224. } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
  225. this._bodyArrayBuffer = bufferClone(body)
  226. } else {
  227. this._bodyText = body = Object.prototype.toString.call(body)
  228. }
  229. if (!this.headers.get('content-type')) {
  230. if (typeof body === 'string') {
  231. this.headers.set('content-type', 'text/plain;charset=UTF-8')
  232. } else if (this._bodyBlob && this._bodyBlob.type) {
  233. this.headers.set('content-type', this._bodyBlob.type)
  234. } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
  235. this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
  236. }
  237. }
  238. }
  239. if (support.blob) {
  240. this.blob = function() {
  241. var rejected = consumed(this)
  242. if (rejected) {
  243. return rejected
  244. }
  245. if (this._bodyBlob) {
  246. return Promise.resolve(this._bodyBlob)
  247. } else if (this._bodyArrayBuffer) {
  248. return Promise.resolve(new Blob([this._bodyArrayBuffer]))
  249. } else if (this._bodyFormData) {
  250. throw new Error('could not read FormData body as blob')
  251. } else {
  252. return Promise.resolve(new Blob([this._bodyText]))
  253. }
  254. }
  255. }
  256. this.arrayBuffer = function() {
  257. if (this._bodyArrayBuffer) {
  258. var isConsumed = consumed(this)
  259. if (isConsumed) {
  260. return isConsumed
  261. } else if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
  262. return Promise.resolve(
  263. this._bodyArrayBuffer.buffer.slice(
  264. this._bodyArrayBuffer.byteOffset,
  265. this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
  266. )
  267. )
  268. } else {
  269. return Promise.resolve(this._bodyArrayBuffer)
  270. }
  271. } else if (support.blob) {
  272. return this.blob().then(readBlobAsArrayBuffer)
  273. } else {
  274. throw new Error('could not read as ArrayBuffer')
  275. }
  276. }
  277. this.text = function() {
  278. var rejected = consumed(this)
  279. if (rejected) {
  280. return rejected
  281. }
  282. if (this._bodyBlob) {
  283. return readBlobAsText(this._bodyBlob)
  284. } else if (this._bodyArrayBuffer) {
  285. return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
  286. } else if (this._bodyFormData) {
  287. throw new Error('could not read FormData body as text')
  288. } else {
  289. return Promise.resolve(this._bodyText)
  290. }
  291. }
  292. if (support.formData) {
  293. this.formData = function() {
  294. return this.text().then(decode)
  295. }
  296. }
  297. this.json = function() {
  298. return this.text().then(JSON.parse)
  299. }
  300. return this
  301. }
  302. // HTTP methods whose capitalization should be normalized
  303. var methods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']
  304. function normalizeMethod(method) {
  305. var upcased = method.toUpperCase()
  306. return methods.indexOf(upcased) > -1 ? upcased : method
  307. }
  308. export function Request(input, options) {
  309. if (!(this instanceof Request)) {
  310. throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
  311. }
  312. options = options || {}
  313. var body = options.body
  314. if (input instanceof Request) {
  315. if (input.bodyUsed) {
  316. throw new TypeError('Already read')
  317. }
  318. this.url = input.url
  319. this.credentials = input.credentials
  320. if (!options.headers) {
  321. this.headers = new Headers(input.headers)
  322. }
  323. this.method = input.method
  324. this.mode = input.mode
  325. this.signal = input.signal
  326. if (!body && input._bodyInit != null) {
  327. body = input._bodyInit
  328. input.bodyUsed = true
  329. }
  330. } else {
  331. this.url = String(input)
  332. }
  333. this.credentials = options.credentials || this.credentials || 'same-origin'
  334. if (options.headers || !this.headers) {
  335. this.headers = new Headers(options.headers)
  336. }
  337. this.method = normalizeMethod(options.method || this.method || 'GET')
  338. this.mode = options.mode || this.mode || null
  339. this.signal = options.signal || this.signal || (function () {
  340. if ('AbortController' in g) {
  341. var ctrl = new AbortController();
  342. return ctrl.signal;
  343. }
  344. }());
  345. this.referrer = null
  346. if ((this.method === 'GET' || this.method === 'HEAD') && body) {
  347. throw new TypeError('Body not allowed for GET or HEAD requests')
  348. }
  349. this._initBody(body)
  350. if (this.method === 'GET' || this.method === 'HEAD') {
  351. if (options.cache === 'no-store' || options.cache === 'no-cache') {
  352. // Search for a '_' parameter in the query string
  353. var reParamSearch = /([?&])_=[^&]*/
  354. if (reParamSearch.test(this.url)) {
  355. // If it already exists then set the value with the current time
  356. this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime())
  357. } else {
  358. // Otherwise add a new '_' parameter to the end with the current time
  359. var reQueryString = /\?/
  360. this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime()
  361. }
  362. }
  363. }
  364. }
  365. Request.prototype.clone = function() {
  366. return new Request(this, {body: this._bodyInit})
  367. }
  368. function decode(body) {
  369. var form = new FormData()
  370. body
  371. .trim()
  372. .split('&')
  373. .forEach(function(bytes) {
  374. if (bytes) {
  375. var split = bytes.split('=')
  376. var name = split.shift().replace(/\+/g, ' ')
  377. var value = split.join('=').replace(/\+/g, ' ')
  378. form.append(decodeURIComponent(name), decodeURIComponent(value))
  379. }
  380. })
  381. return form
  382. }
  383. function parseHeaders(rawHeaders) {
  384. var headers = new Headers()
  385. // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
  386. // https://tools.ietf.org/html/rfc7230#section-3.2
  387. var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
  388. // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
  389. // https://github.com/github/fetch/issues/748
  390. // https://github.com/zloirock/core-js/issues/751
  391. preProcessedHeaders
  392. .split('\r')
  393. .map(function(header) {
  394. return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header
  395. })
  396. .forEach(function(line) {
  397. var parts = line.split(':')
  398. var key = parts.shift().trim()
  399. if (key) {
  400. var value = parts.join(':').trim()
  401. try {
  402. headers.append(key, value)
  403. } catch (error) {
  404. console.warn('Response ' + error.message)
  405. }
  406. }
  407. })
  408. return headers
  409. }
  410. Body.call(Request.prototype)
  411. export function Response(bodyInit, options) {
  412. if (!(this instanceof Response)) {
  413. throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
  414. }
  415. if (!options) {
  416. options = {}
  417. }
  418. this.type = 'default'
  419. this.status = options.status === undefined ? 200 : options.status
  420. if (this.status < 200 || this.status > 599) {
  421. throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].")
  422. }
  423. this.ok = this.status >= 200 && this.status < 300
  424. this.statusText = options.statusText === undefined ? '' : '' + options.statusText
  425. this.headers = new Headers(options.headers)
  426. this.url = options.url || ''
  427. this._initBody(bodyInit)
  428. }
  429. Body.call(Response.prototype)
  430. Response.prototype.clone = function() {
  431. return new Response(this._bodyInit, {
  432. status: this.status,
  433. statusText: this.statusText,
  434. headers: new Headers(this.headers),
  435. url: this.url
  436. })
  437. }
  438. Response.error = function() {
  439. var response = new Response(null, {status: 200, statusText: ''})
  440. response.status = 0
  441. response.type = 'error'
  442. return response
  443. }
  444. var redirectStatuses = [301, 302, 303, 307, 308]
  445. Response.redirect = function(url, status) {
  446. if (redirectStatuses.indexOf(status) === -1) {
  447. throw new RangeError('Invalid status code')
  448. }
  449. return new Response(null, {status: status, headers: {location: url}})
  450. }
  451. export var DOMException = g.DOMException
  452. try {
  453. new DOMException()
  454. } catch (err) {
  455. DOMException = function(message, name) {
  456. this.message = message
  457. this.name = name
  458. var error = Error(message)
  459. this.stack = error.stack
  460. }
  461. DOMException.prototype = Object.create(Error.prototype)
  462. DOMException.prototype.constructor = DOMException
  463. }
  464. export function fetch(input, init) {
  465. return new Promise(function(resolve, reject) {
  466. var request = new Request(input, init)
  467. if (request.signal && request.signal.aborted) {
  468. return reject(new DOMException('Aborted', 'AbortError'))
  469. }
  470. var xhr = new XMLHttpRequest()
  471. function abortXhr() {
  472. xhr.abort()
  473. }
  474. xhr.onload = function() {
  475. var options = {
  476. statusText: xhr.statusText,
  477. headers: parseHeaders(xhr.getAllResponseHeaders() || '')
  478. }
  479. // This check if specifically for when a user fetches a file locally from the file system
  480. // Only if the status is out of a normal range
  481. if (request.url.startsWith('file://') && (xhr.status < 200 || xhr.status > 599)) {
  482. options.status = 200;
  483. } else {
  484. options.status = xhr.status;
  485. }
  486. options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
  487. var body = 'response' in xhr ? xhr.response : xhr.responseText
  488. setTimeout(function() {
  489. resolve(new Response(body, options))
  490. }, 0)
  491. }
  492. xhr.onerror = function() {
  493. setTimeout(function() {
  494. reject(new TypeError('Network request failed'))
  495. }, 0)
  496. }
  497. xhr.ontimeout = function() {
  498. setTimeout(function() {
  499. reject(new TypeError('Network request failed'))
  500. }, 0)
  501. }
  502. xhr.onabort = function() {
  503. setTimeout(function() {
  504. reject(new DOMException('Aborted', 'AbortError'))
  505. }, 0)
  506. }
  507. function fixUrl(url) {
  508. try {
  509. return url === '' && g.location.href ? g.location.href : url
  510. } catch (e) {
  511. return url
  512. }
  513. }
  514. xhr.open(request.method, fixUrl(request.url), true)
  515. if (request.credentials === 'include') {
  516. xhr.withCredentials = true
  517. } else if (request.credentials === 'omit') {
  518. xhr.withCredentials = false
  519. }
  520. if ('responseType' in xhr) {
  521. if (support.blob) {
  522. xhr.responseType = 'blob'
  523. } else if (
  524. support.arrayBuffer
  525. ) {
  526. xhr.responseType = 'arraybuffer'
  527. }
  528. }
  529. if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers || (g.Headers && init.headers instanceof g.Headers))) {
  530. var names = [];
  531. Object.getOwnPropertyNames(init.headers).forEach(function(name) {
  532. names.push(normalizeName(name))
  533. xhr.setRequestHeader(name, normalizeValue(init.headers[name]))
  534. })
  535. request.headers.forEach(function(value, name) {
  536. if (names.indexOf(name) === -1) {
  537. xhr.setRequestHeader(name, value)
  538. }
  539. })
  540. } else {
  541. request.headers.forEach(function(value, name) {
  542. xhr.setRequestHeader(name, value)
  543. })
  544. }
  545. if (request.signal) {
  546. request.signal.addEventListener('abort', abortXhr)
  547. xhr.onreadystatechange = function() {
  548. // DONE (success or failure)
  549. if (xhr.readyState === 4) {
  550. request.signal.removeEventListener('abort', abortXhr)
  551. }
  552. }
  553. }
  554. xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
  555. })
  556. }
  557. fetch.polyfill = true
  558. if (!g.fetch) {
  559. g.fetch = fetch
  560. g.Headers = Headers
  561. g.Request = Request
  562. g.Response = Response
  563. }