fetch.js 16 KB

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