UNPKG

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