pax_global_header00006660000000000000000000000064133444700340014514gustar00rootroot0000000000000052 comment=cc84bc284bd2d8eeddaf1c6a68e02e5329482f97 fetch-3.0.0/000077500000000000000000000000001334447003400126055ustar00rootroot00000000000000fetch-3.0.0/.eslintrc.json000066400000000000000000000013461334447003400154050ustar00rootroot00000000000000{ "parserOptions": { "sourceType": "module" }, "globals": { "WHATWGFetch": true, "ArrayBuffer": true, "DataView": true, "Promise": true, "Symbol": true, "Uint8Array": true }, "extends": [ "plugin:github/browser" ], "rules": { "object-shorthand": "off" }, "overrides": [ { "files": ["test/*.js"], "env": { "browser": true, "mocha": true }, "globals": { "assert": true, "chai": true, "FileReaderSync": true } }, { "files": ["test/{karma,server}*.js"], "env": { "node": true } }, { "files": ["test/worker.js"], "env": { "worker": true } } ] } fetch-3.0.0/.gitignore000066400000000000000000000001361334447003400145750ustar00rootroot00000000000000.env package-lock.json dist/ bower_components/ node_modules/ sauce_connect/ sauce_connect.log fetch-3.0.0/.travis.yml000066400000000000000000000006741334447003400147250ustar00rootroot00000000000000sudo: false language: node_js node_js: - "node" dist: trusty addons: chrome: stable firefox: latest cache: directories: - node_modules deploy: provider: npm email: mislav.marohnic@gmail.com api_key: secure: gt9g5/bXhxSKjxfFSPCdpWGJKBrSG8zdGRYgPouUgRqNeD2Ff4Nc8HGQTxp0OLKnP/jJ5FIru5jUur6LWzJCyEd+aNUEvFf5J078m3pzHN9AP2fiWUkKXcc5lKV0PQnI+JDRxJwd/PggtjubrneGfCzyFoys9apRrd/TzTGEtGw= on: tags: true repo: github/fetch fetch-3.0.0/CODE_OF_CONDUCT.md000066400000000000000000000062301334447003400154050ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource+fetch@github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ fetch-3.0.0/CONTRIBUTING.md000066400000000000000000000042431334447003400150410ustar00rootroot00000000000000# Contributing Thank you for your interest in contributing to our `fetch` polyfill! Note that we only accept features that are also described in the official [fetch specification][]. However, the aim of this project is not to implement the complete specification; just the parts that are possible to emulate using XMLHttpRequest. Contributions to this project are [released][tos] to the public under the [project's open source license](LICENSE). ## Running tests Running `npm test` will: 1. Build the `dist/` files; 1. Run the test suite in headless Chrome & Firefox; 1. Run the same test suite in Web Worker mode. When editing tests or implementation, keep `npm run karma` running: - You can connect additional browsers by navigating to `http://localhost:9876/`; - Changes to [test.js](test/test.js) will automatically re-run the tests in all connected browsers; - When changing [fetch.js](fetch.js), re-run tests by executing `make`; - Re-run specific tests with `./node_modules/.bin/karma run -- --grep=`. ## Submitting a pull request 1. [Fork][fork] and clone the repository; 1. Create a new branch: `git checkout -b my-branch-name`; 1. Make your change, push to your fork and [submit a pull request][pr]; 1. Pat your self on the back and wait for your pull request to be reviewed. Here are a few things you can do that will increase the likelihood of your pull request being accepted: - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. - Write a [good commit message][]. ## Resources - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) - [GitHub Help](https://help.github.com) [fetch specification]: https://fetch.spec.whatwg.org [tos]: https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license [fork]: https://github.com/github/fetch/fork [pr]: https://github.com/github/fetch/compare [good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html fetch-3.0.0/LICENSE000066400000000000000000000020451334447003400136130ustar00rootroot00000000000000Copyright (c) 2014-2016 GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fetch-3.0.0/Makefile000066400000000000000000000005641334447003400142520ustar00rootroot00000000000000test: lint dist/fetch.umd.js lint: node_modules/ ./node_modules/.bin/eslint --report-unused-disable-directives *.js test/*.js dist/fetch.umd.js: fetch.js rollup.config.js node_modules/ ./node_modules/.bin/rollup -c dist/fetch.umd.js.flow: fetch.js.flow cp $< $@ node_modules/: npm install clean: rm -rf ./bower_components ./node_modules .PHONY: clean lint test fetch-3.0.0/README.md000066400000000000000000000231771334447003400140760ustar00rootroot00000000000000# window.fetch polyfill The `fetch()` function is a Promise-based mechanism for programmatically making web requests in the browser. This project is a polyfill that implements a subset of the standard [Fetch specification][], enough to make `fetch` a viable replacement for most uses of XMLHttpRequest in traditional web applications. ## Table of Contents * [Read this first](#read-this-first) * [Installation](#installation) * [Usage](#usage) * [Importing](#importing) * [HTML](#html) * [JSON](#json) * [Response metadata](#response-metadata) * [Post form](#post-form) * [Post JSON](#post-json) * [File upload](#file-upload) * [Caveats](#caveats) * [Handling HTTP error statuses](#handling-http-error-statuses) * [Sending cookies](#sending-cookies) * [Receiving cookies](#receiving-cookies) * [Obtaining the Response URL](#obtaining-the-response-url) * [Aborting requests](#aborting-requests) * [Browser Support](#browser-support) ## Read this first * If you believe you found a bug with how `fetch` behaves in your browser, please **don't open an issue in this repository** unless you are testing in an old version of a browser that doesn't support `window.fetch` natively. This project is a _polyfill_, and since all modern browsers now implement the `fetch` function natively, **no code from this project** actually takes any effect there. See [Browser support](#browser-support) for detailed information. * If you have trouble **making a request to another domain** (a different subdomain or port number also constitutes another domain), please familiarize yourself with all the intricacies and limitations of [CORS][] requests. Because CORS requires participation of the server by implementing specific HTTP response headers, it is often nontrivial to set up or debug. CORS is exclusively handled by the browser's internal mechanisms which this polyfill cannot influence. * This project **doesn't work under Node.js environments**. It's meant for web browsers only. You should ensure that your application doesn't try to package and run this on the server. * If you have an idea for a new feature of `fetch`, **submit your feature requests** to the [specification's repository](https://github.com/whatwg/fetch/issues). We only add features and APIs that are part of the [Fetch specification][]. ## Installation ``` npm install whatwg-fetch --save ``` You will also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises). We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill) for its small size and Promises/A+ compatibility. ## Usage For a more comprehensive API reference that this polyfill supports, refer to https://github.github.io/fetch/. ### Importing Importing will automatically polyfill `window.fetch` and related APIs: ```javascript import 'whatwg-fetch' window.fetch(...) ``` If for some reason you need to access the polyfill implementation, it is available via exports: ```javascript import {fetch as fetchPolyfill} from 'whatwg-fetch' window.fetch(...) // use native browser version fetchPolyfill(...) // use polyfill implementation ``` This approach can be used to, for example, use [abort functionality](#aborting-requests) in browsers that implement a native but outdated version of fetch that doesn't support aborting. For use with webpack, add this package in the `entry` configuration option before your application entry point: ```javascript entry: ['whatwg-fetch', ...] ``` ### HTML ```javascript fetch('/users.html') .then(function(response) { return response.text() }).then(function(body) { document.body.innerHTML = body }) ``` ### JSON ```javascript fetch('/users.json') .then(function(response) { return response.json() }).then(function(json) { console.log('parsed json', json) }).catch(function(ex) { console.log('parsing failed', ex) }) ``` ### Response metadata ```javascript fetch('/users.json').then(function(response) { console.log(response.headers.get('Content-Type')) console.log(response.headers.get('Date')) console.log(response.status) console.log(response.statusText) }) ``` ### Post form ```javascript var form = document.querySelector('form') fetch('/users', { method: 'POST', body: new FormData(form) }) ``` ### Post JSON ```javascript fetch('/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Hubot', login: 'hubot', }) }) ``` ### File upload ```javascript var input = document.querySelector('input[type="file"]') var data = new FormData() data.append('file', input.files[0]) data.append('user', 'hubot') fetch('/avatars', { method: 'POST', body: data }) ``` ### Caveats * The Promise returned from `fetch()` **won't reject on HTTP error status** even if the response is an HTTP 404 or 500. Instead, it will resolve normally, and it will only reject on network failure or if anything prevented the request from completing. * For maximum browser compatibility when it comes to sending & receiving cookies, always supply the `credentials: 'same-origin'` option instead of relying on the default. See [Sending cookies](#sending-cookies). #### Handling HTTP error statuses To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx status, define a custom response handler: ```javascript function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response } else { var error = new Error(response.statusText) error.response = response throw error } } function parseJSON(response) { return response.json() } fetch('/users') .then(checkStatus) .then(parseJSON) .then(function(data) { console.log('request succeeded with JSON response', data) }).catch(function(error) { console.log('request failed', error) }) ``` #### Sending cookies For [CORS][] requests, use `credentials: 'include'` to allow sending credentials to other domains: ```javascript fetch('https://example.com:1234/users', { credentials: 'include' }) ``` To disable sending or receiving cookies for requests to any domain, including the current one, use the "omit" value: ```javascript fetch('/users', { credentials: 'omit' }) ``` The default value for `credentials` is "same-origin". The default for `credentials` wasn't always the same, though. The following versions of browsers implemented an older version of the fetch specification where the default was "omit": * Firefox 39-60 * Chrome 42-67 * Safari 10.1-11.1.2 If you target these browsers, it's advisable to always specify `credentials: 'same-origin'` explicitly with all fetch requests instead of relying on the default: ```javascript fetch('/users', { credentials: 'same-origin' }) ``` #### Receiving cookies As with XMLHttpRequest, the `Set-Cookie` response header returned from the server is a [forbidden header name][] and therefore can't be programmatically read with `response.headers.get()`. Instead, it's the browser's responsibility to handle new cookies being set (if applicable to the current URL). Unless they are HTTP-only, new cookies will be available through `document.cookie`. #### Obtaining the Response URL Due to limitations of XMLHttpRequest, the `response.url` value might not be reliable after HTTP redirects on older browsers. The solution is to configure the server to set the response HTTP header `X-Request-URL` to the current URL after any redirect that might have happened. It should be safe to set it unconditionally. ``` ruby # Ruby on Rails controller example response.headers['X-Request-URL'] = request.url ``` This server workaround is necessary if you need reliable `response.url` in Firefox < 32, Chrome < 37, Safari, or IE. #### Aborting requests This polyfill supports [the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch). However, aborting a fetch requires use of two additional DOM APIs: [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). Typically, browsers that do not support fetch will also not support AbortController or AbortSignal. Consequently, you will need to include [an additional polyfill](https://github.com/mo/abortcontroller-polyfill#readme) for these APIs to abort fetches: ```js import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only' import {fetch} from 'whatwg-fetch' // use native browser implementation if it supports aborting const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch const controller = new AbortController() abortableFetch('/avatars', { signal: controller.signal }).catch(function(ex) { if (ex.name === 'AbortError') { console.log('request aborted') } }) // some time later... controller.abort() ``` ## Browser Support - Chrome - Firefox - Safari 6.1+ - Internet Explorer 10+ Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native implementations of `window.fetch`, therefore the code from this polyfill doesn't have any effect on those browsers. If you believe you've encountered an error with how `window.fetch` is implemented in any of these browsers, you should file an issue with that browser vendor instead of this project. [fetch specification]: https://fetch.spec.whatwg.org [cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS "Cross-origin resource sharing" [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet "Cross-site request forgery" [forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name fetch-3.0.0/bower.json000066400000000000000000000002451334447003400146170ustar00rootroot00000000000000{ "name": "fetch", "main": "fetch.js", "ignore": [ ".*", "*.md", "examples/", "Makefile", "package.json", "script/", "test/" ] } fetch-3.0.0/fetch.js000066400000000000000000000317161334447003400142440ustar00rootroot00000000000000var support = { searchParams: 'URLSearchParams' in self, iterable: 'Symbol' in self && 'iterator' in Symbol, blob: 'FileReader' in self && 'Blob' in self && (function() { try { new Blob() return true } catch (e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self } function isDataView(obj) { return obj && DataView.prototype.isPrototypeOf(obj) } if (support.arrayBuffer) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ] var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } } function normalizeName(name) { if (typeof name !== 'string') { name = String(name) } if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) { throw new TypeError('Invalid character in header field name') } return name.toLowerCase() } function normalizeValue(value) { if (typeof value !== 'string') { value = String(value) } return value } // Build a destructive iterator for the value list function iteratorFor(items) { var iterator = { next: function() { var value = items.shift() return {done: value === undefined, value: value} } } if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator } } return iterator } export function Headers(headers) { this.map = {} if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value) }, this) } else if (Array.isArray(headers)) { headers.forEach(function(header) { this.append(header[0], header[1]) }, this) } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]) }, this) } } Headers.prototype.append = function(name, value) { name = normalizeName(name) value = normalizeValue(value) var oldValue = this.map[name] this.map[name] = oldValue ? oldValue + ', ' + value : value } Headers.prototype['delete'] = function(name) { delete this.map[normalizeName(name)] } Headers.prototype.get = function(name) { name = normalizeName(name) return this.has(name) ? this.map[name] : null } Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)) } Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value) } Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this) } } } Headers.prototype.keys = function() { var items = [] this.forEach(function(value, name) { items.push(name) }) return iteratorFor(items) } Headers.prototype.values = function() { var items = [] this.forEach(function(value) { items.push(value) }) return iteratorFor(items) } Headers.prototype.entries = function() { var items = [] this.forEach(function(value, name) { items.push([name, value]) }) return iteratorFor(items) } if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries } function consumed(body) { if (body.bodyUsed) { return Promise.reject(new TypeError('Already read')) } body.bodyUsed = true } function fileReaderReady(reader) { return new Promise(function(resolve, reject) { reader.onload = function() { resolve(reader.result) } reader.onerror = function() { reject(reader.error) } }) } function readBlobAsArrayBuffer(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsArrayBuffer(blob) return promise } function readBlobAsText(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsText(blob) return promise } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf) var chars = new Array(view.length) for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]) } return chars.join('') } function bufferClone(buf) { if (buf.slice) { return buf.slice(0) } else { var view = new Uint8Array(buf.byteLength) view.set(new Uint8Array(buf)) return view.buffer } } function Body() { this.bodyUsed = false this._initBody = function(body) { this._bodyInit = body if (!body) { this._bodyText = '' } else if (typeof body === 'string') { this._bodyText = body } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString() } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer) // IE 10-11 can't handle a DataView body. this._bodyInit = new Blob([this._bodyArrayBuffer]) } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body) } else { this._bodyText = body = Object.prototype.toString.call(body) } if (!this.headers.get('content-type')) { if (typeof body === 'string') { this.headers.set('content-type', 'text/plain;charset=UTF-8') } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set('content-type', this._bodyBlob.type) } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') } } } if (support.blob) { this.blob = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } } this.arrayBuffer = function() { if (this._bodyArrayBuffer) { return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { return this.blob().then(readBlobAsArrayBuffer) } } } this.text = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { throw new Error('could not read FormData body as text') } else { return Promise.resolve(this._bodyText) } } if (support.formData) { this.formData = function() { return this.text().then(decode) } } this.json = function() { return this.text().then(JSON.parse) } return this } // HTTP methods whose capitalization should be normalized var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] function normalizeMethod(method) { var upcased = method.toUpperCase() return methods.indexOf(upcased) > -1 ? upcased : method } export function Request(input, options) { options = options || {} var body = options.body if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError('Already read') } this.url = input.url this.credentials = input.credentials if (!options.headers) { this.headers = new Headers(input.headers) } this.method = input.method this.mode = input.mode this.signal = input.signal if (!body && input._bodyInit != null) { body = input._bodyInit input.bodyUsed = true } } else { this.url = String(input) } this.credentials = options.credentials || this.credentials || 'same-origin' if (options.headers || !this.headers) { this.headers = new Headers(options.headers) } this.method = normalizeMethod(options.method || this.method || 'GET') this.mode = options.mode || this.mode || null this.signal = options.signal || this.signal this.referrer = null if ((this.method === 'GET' || this.method === 'HEAD') && body) { throw new TypeError('Body not allowed for GET or HEAD requests') } this._initBody(body) } Request.prototype.clone = function() { return new Request(this, {body: this._bodyInit}) } function decode(body) { var form = new FormData() body .trim() .split('&') .forEach(function(bytes) { if (bytes) { var split = bytes.split('=') var name = split.shift().replace(/\+/g, ' ') var value = split.join('=').replace(/\+/g, ' ') form.append(decodeURIComponent(name), decodeURIComponent(value)) } }) return form } function parseHeaders(rawHeaders) { var headers = new Headers() // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space // https://tools.ietf.org/html/rfc7230#section-3.2 var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ') preProcessedHeaders.split(/\r?\n/).forEach(function(line) { var parts = line.split(':') var key = parts.shift().trim() if (key) { var value = parts.join(':').trim() headers.append(key, value) } }) return headers } Body.call(Request.prototype) export function Response(bodyInit, options) { if (!options) { options = {} } this.type = 'default' this.status = options.status === undefined ? 200 : options.status this.ok = this.status >= 200 && this.status < 300 this.statusText = 'statusText' in options ? options.statusText : 'OK' this.headers = new Headers(options.headers) this.url = options.url || '' this._initBody(bodyInit) } Body.call(Response.prototype) Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }) } Response.error = function() { var response = new Response(null, {status: 0, statusText: ''}) response.type = 'error' return response } var redirectStatuses = [301, 302, 303, 307, 308] Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError('Invalid status code') } return new Response(null, {status: status, headers: {location: url}}) } export var DOMException = self.DOMException try { new DOMException() } catch (err) { DOMException = function(message, name) { this.message = message this.name = name var error = Error(message) this.stack = error.stack } DOMException.prototype = Object.create(Error.prototype) DOMException.prototype.constructor = DOMException } export function fetch(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init) if (request.signal && request.signal.aborted) { return reject(new DOMException('Aborted', 'AbortError')) } var xhr = new XMLHttpRequest() function abortXhr() { xhr.abort() } xhr.onload = function() { var options = { status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || '') } options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') var body = 'response' in xhr ? xhr.response : xhr.responseText resolve(new Response(body, options)) } xhr.onerror = function() { reject(new TypeError('Network request failed')) } xhr.ontimeout = function() { reject(new TypeError('Network request failed')) } xhr.onabort = function() { reject(new DOMException('Aborted', 'AbortError')) } xhr.open(request.method, request.url, true) if (request.credentials === 'include') { xhr.withCredentials = true } else if (request.credentials === 'omit') { xhr.withCredentials = false } if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob' } request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value) }) if (request.signal) { request.signal.addEventListener('abort', abortXhr) xhr.onreadystatechange = function() { // DONE (success or failure) if (xhr.readyState === 4) { request.signal.removeEventListener('abort', abortXhr) } } } xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) }) } fetch.polyfill = true if (!self.fetch) { self.fetch = fetch self.Headers = Headers self.Request = Request self.Response = Response } fetch-3.0.0/fetch.js.flow000066400000000000000000000061301334447003400152020ustar00rootroot00000000000000/* @flow strict */ type CredentialsType = 'omit' | 'same-origin' | 'include' type ResponseType = 'default' | 'error' type BodyInit = string | URLSearchParams | FormData | Blob | ArrayBuffer | $ArrayBufferView type RequestInfo = Request | URL | string type RequestOptions = {| body?: ?BodyInit; credentials?: CredentialsType; headers?: HeadersInit; method?: string; mode?: string; referrer?: string; signal?: ?AbortSignal; |} type ResponseOptions = {| status?: number; statusText?: string; headers?: HeadersInit; |} type HeadersInit = Headers | {[string]: string} // https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L902-L914 declare class Headers { @@iterator(): Iterator<[string, string]>; constructor(init?: HeadersInit): void; append(name: string, value: string): void; delete(name: string): void; entries(): Iterator<[string, string]>; forEach((value: string, name: string, headers: Headers) => any, thisArg?: any): void; get(name: string): null | string; has(name: string): boolean; keys(): Iterator; set(name: string, value: string): void; values(): Iterator; } // https://github.com/facebook/flow/pull/6548 interface AbortSignal { aborted: boolean; addEventListener(type: string, listener: (Event) => mixed, options?: EventListenerOptionsOrUseCapture): void; removeEventListener(type: string, listener: (Event) => mixed, options?: EventListenerOptionsOrUseCapture): void; } // https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L994-L1018 // unsupported in polyfill: // - cache // - integrity // - redirect // - referrerPolicy declare class Request { constructor(input: RequestInfo, init?: RequestOptions): void; clone(): Request; url: string; credentials: CredentialsType; headers: Headers; method: string; mode: ModeType; referrer: string; signal: ?AbortSignal; // Body methods and attributes bodyUsed: boolean; arrayBuffer(): Promise; blob(): Promise; formData(): Promise; json(): Promise; text(): Promise; } // https://github.com/facebook/flow/blob/f68b89a5012bd995ab3509e7a41b7325045c4045/lib/bom.js#L968-L992 // unsupported in polyfill: // - body // - redirected // - trailer declare class Response { constructor(input?: ?BodyInit, init?: ResponseOptions): void; clone(): Response; static error(): Response; static redirect(url: string, status?: number): Response; type: ResponseType; url: string; ok: boolean; status: number; statusText: string; headers: Headers; // Body methods and attributes bodyUsed: boolean; arrayBuffer(): Promise; blob(): Promise; formData(): Promise; json(): Promise; text(): Promise; } declare class DOMException extends Error { constructor(message?: string, name?: string): void; } declare module.exports: { fetch(input: RequestInfo, init?: RequestOptions): Promise; Headers: typeof Headers; Request: typeof Request; Response: typeof Response; DOMException: typeof DOMException; } fetch-3.0.0/package.json000066400000000000000000000022171334447003400150750ustar00rootroot00000000000000{ "name": "whatwg-fetch", "description": "A window.fetch polyfill.", "version": "3.0.0", "main": "./dist/fetch.umd.js", "module": "./fetch.js", "repository": "github/fetch", "license": "MIT", "devDependencies": { "abortcontroller-polyfill": "^1.1.9", "chai": "^4.1.2", "eslint": "^4.19.1", "eslint-plugin-github": "^1.0.0", "karma": "^3.0.0", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^2.2.0", "karma-detect-browsers": "^2.3.2", "karma-firefox-launcher": "^1.1.0", "karma-mocha": "^1.3.0", "karma-safari-launcher": "^1.0.0", "karma-safaritechpreview-launcher": "0.0.6", "mocha": "^4.0.1", "promise-polyfill": "6.0.2", "rollup": "^0.59.1", "url-search-params": "0.6.1" }, "files": [ "LICENSE", "dist/fetch.umd.js", "dist/fetch.umd.js.flow", "fetch.js", "fetch.js.flow" ], "scripts": { "karma": "karma start ./test/karma.config.js --no-single-run --auto-watch", "prepare": "make dist/fetch.umd.js dist/fetch.umd.js.flow", "pretest": "make", "test": "karma start ./test/karma.config.js && karma start ./test/karma-worker.config.js" } } fetch-3.0.0/prettier.config.js000066400000000000000000000001011334447003400162350ustar00rootroot00000000000000module.exports = require('eslint-plugin-github/prettier.config') fetch-3.0.0/rollup.config.js000066400000000000000000000002021334447003400157160ustar00rootroot00000000000000export default { input: 'fetch.js', output: { file: 'dist/fetch.umd.js', format: 'umd', name: 'WHATWGFetch' } } fetch-3.0.0/test/000077500000000000000000000000001334447003400135645ustar00rootroot00000000000000fetch-3.0.0/test/karma-worker.config.js000066400000000000000000000007001334447003400177650ustar00rootroot00000000000000const parentConfig = require('./karma.config') module.exports = function(config) { parentConfig(config) config.set({ frameworks: ['detectBrowsers', 'mocha'], files: [ 'test/worker-adapter.js', { pattern: '{test,dist}/*.js', included: false }, { pattern: 'node_modules/{mocha,chai,abortcontroller-polyfill/dist}/*.js', included: false, watched: false } ] }) } fetch-3.0.0/test/karma.config.js000066400000000000000000000030121334447003400164550ustar00rootroot00000000000000const serverEndpoints = require('./server') module.exports = function(config) { config.set({ basePath: '..', frameworks: ['detectBrowsers', 'mocha', 'chai'], detectBrowsers: { preferHeadless: true, usePhantomJS: false, postDetection: availableBrowsers => availableBrowsers .filter( browser => !process.env.CI || !browser.startsWith('Chromium') || !availableBrowsers.some(b => b.startsWith('Chrome')) ) .map(browser => (browser.startsWith('Chrom') ? `${browser}NoSandbox` : browser)) }, client: { mocha: { ui: 'tdd' } }, files: [ 'node_modules/promise-polyfill/promise.js', 'node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js', 'node_modules/url-search-params/build/url-search-params.max.js', 'dist/fetch.umd.js', 'test/test.js' ], reporters: process.env.CI ? ['dots'] : ['progress'], port: 9876, colors: true, logLevel: process.env.CI ? config.LOG_WARN : config.LOG_INFO, autoWatch: false, singleRun: true, concurrency: Infinity, customLaunchers: { ChromeHeadlessNoSandbox: { base: 'ChromeHeadless', flags: ['--no-sandbox'] }, ChromiumHeadlessNoSandbox: { base: 'ChromiumHeadless', flags: ['--no-sandbox'] } }, beforeMiddleware: ['custom'], plugins: [ 'karma-*', { 'middleware:custom': ['value', serverEndpoints] } ] }) } fetch-3.0.0/test/server.js000066400000000000000000000065571334447003400154450ustar00rootroot00000000000000const url = require('url') const querystring = require('querystring') const routes = { '/request': function(res, req) { res.writeHead(200, {'Content-Type': 'application/json'}) var data = '' req.on('data', function(c) { data += c }) req.on('end', function() { res.end( JSON.stringify({ method: req.method, url: req.url, headers: req.headers, data: data }) ) }) }, '/hello': function(res, req) { res.writeHead(200, { 'Content-Type': 'text/plain', 'X-Request-URL': 'http://' + req.headers.host + req.url }) res.end('hi') }, '/hello/utf8': function(res) { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }) // "hello" var buf = Buffer.from([104, 101, 108, 108, 111]) res.end(buf) }, '/hello/utf16le': function(res) { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-16le' }) // "hello" var buf = Buffer.from([104, 0, 101, 0, 108, 0, 108, 0, 111, 0]) res.end(buf) }, '/binary': function(res) { res.writeHead(200, {'Content-Type': 'application/octet-stream'}) var buf = Buffer.alloc(256) for (var i = 0; i < 256; i++) { buf[i] = i } res.end(buf) }, '/redirect/301': function(res) { res.writeHead(301, {Location: '/hello'}) res.end() }, '/redirect/302': function(res) { res.writeHead(302, {Location: '/hello'}) res.end() }, '/redirect/303': function(res) { res.writeHead(303, {Location: '/hello'}) res.end() }, '/redirect/307': function(res) { res.writeHead(307, {Location: '/hello'}) res.end() }, '/redirect/308': function(res) { res.writeHead(308, {Location: '/hello'}) res.end() }, '/boom': function(res) { res.writeHead(500, {'Content-Type': 'text/plain'}) res.end('boom') }, '/empty': function(res) { res.writeHead(204) res.end() }, '/slow': function(res) { setTimeout(function() { res.writeHead(200, {'Cache-Control': 'no-cache, must-revalidate'}) res.end() }, 100) }, '/error': function(res) { res.destroy() }, '/form': function(res) { res.writeHead(200, {'Content-Type': 'application/x-www-form-urlencoded'}) res.end('number=1&space=one+two&empty=&encoded=a%2Bb&') }, '/json': function(res) { res.writeHead(200, {'Content-Type': 'application/json'}) res.end(JSON.stringify({name: 'Hubot', login: 'hubot'})) }, '/json-error': function(res) { res.writeHead(200, {'Content-Type': 'application/json'}) res.end('not json {') }, '/cookie': function(res, req) { var setCookie, cookie var params = querystring.parse(url.parse(req.url).query) if (params.name && params.value) { setCookie = [params.name, params.value].join('=') } if (params.name) { cookie = querystring.parse(req.headers['cookie'], '; ')[params.name] } res.writeHead(200, { 'Content-Type': 'text/plain', 'Set-Cookie': setCookie || '' }) res.end(cookie) }, '/headers': function(res) { res.writeHead(200, { Date: 'Mon, 13 Oct 2014 21:02:27 GMT', 'Content-Type': 'text/html; charset=utf-8' }) res.end() } } module.exports = function(req, res, next) { const path = url.parse(req.url).pathname const route = routes[path] if (route) { route(res, req) } else { next() } } fetch-3.0.0/test/test.js000066400000000000000000001423371334447003400151130ustar00rootroot00000000000000var IEorEdge = /Edge\//.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent) var Chrome = /Chrome\//.test(navigator.userAgent) && !IEorEdge var Safari = /Safari\//.test(navigator.userAgent) && !IEorEdge && !Chrome var support = { url: (function(url) { try { return new URL(url).toString() === url } catch (e) { return false } })('http://example.com/'), blob: 'FileReader' in self && 'Blob' in self && (function() { try { new Blob() return true } catch (e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self, aborting: 'signal' in new Request(''), permanentRedirect: !/Trident/.test(navigator.userAgent) } function readBlobAsText(blob) { if ('FileReader' in self) { return new Promise(function(resolve, reject) { var reader = new FileReader() reader.onload = function() { resolve(reader.result) } reader.onerror = function() { reject(reader.error) } reader.readAsText(blob) }) } else if ('FileReaderSync' in self) { return new FileReaderSync().readAsText(blob) } else { throw new ReferenceError('FileReader is not defined') } } function readBlobAsBytes(blob) { if ('FileReader' in self) { return new Promise(function(resolve, reject) { var reader = new FileReader() reader.onload = function() { var view = new Uint8Array(reader.result) resolve(Array.prototype.slice.call(view)) } reader.onerror = function() { reject(reader.error) } reader.readAsArrayBuffer(blob) }) } else if ('FileReaderSync' in self) { return new FileReaderSync().readAsArrayBuffer(blob) } else { throw new ReferenceError('FileReader is not defined') } } function arrayBufferFromText(text) { var buf = new ArrayBuffer(text.length) var view = new Uint8Array(buf) for (var i = 0; i < text.length; i++) { view[i] = text.charCodeAt(i) } return buf } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf) var chars = new Array(view.length) for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]) } return chars.join('') } var preservedGlobals = {} var keepGlobals = ['fetch', 'Headers', 'Request', 'Response'] var exercise = ['polyfill'] // If native fetch implementation exists, replace it with the polyfilled // version at first. The native implementation will be restored before the // additional `native` pass of the test suite. if (!self.fetch.polyfill) { keepGlobals.forEach(function(name) { preservedGlobals[name] = self[name] self[name] = WHATWGFetch[name] }) exercise.push('native') } var slice = Array.prototype.slice function featureDependent(testOrSuite, condition) { // eslint-disable-next-line no-invalid-this ;(condition ? testOrSuite : testOrSuite.skip).apply(this, slice.call(arguments, 2)) } exercise.forEach(function(exerciseMode) { suite(exerciseMode, function() { if (exerciseMode === 'native') { suiteSetup(function() { keepGlobals.forEach(function(name) { self[name] = preservedGlobals[name] }) }) } var nativeChrome = Chrome && exerciseMode === 'native' var nativeSafari = Safari && exerciseMode === 'native' var nativeEdge = /Edge\//.test(navigator.userAgent) && exerciseMode === 'native' var firefox = navigator.userAgent.match(/Firefox\/(\d+)/) var brokenFF = firefox && firefox[1] <= 56 && exerciseMode === 'native' var polyfillFirefox = firefox && exerciseMode === 'polyfill' var omitSafari = Safari && exerciseMode === 'native' && navigator.userAgent.match(/Version\/(\d+\.\d+)/)[1] <= '11.1' // https://fetch.spec.whatwg.org/#concept-bodyinit-extract function testBodyExtract(factory) { suite('body extract', function() { var expected = 'Hello World!' var inputs = [['type USVString', expected]] if (support.blob) { inputs.push(['type Blob', new Blob([expected])]) } if (support.arrayBuffer) { inputs = inputs.concat([ ['type ArrayBuffer', arrayBufferFromText(expected)], ['type TypedArray', new Uint8Array(arrayBufferFromText(expected))], ['type DataView', new DataView(arrayBufferFromText(expected))] ]) } inputs.forEach(function(input) { var typeLabel = input[0], body = input[1] suite(typeLabel, function() { featureDependent(test, support.blob, 'consume as blob', function() { var r = factory(body) return r .blob() .then(readBlobAsText) .then(function(text) { assert.equal(text, expected) }) }) test('consume as text', function() { var r = factory(body) return r.text().then(function(text) { assert.equal(text, expected) }) }) featureDependent(test, support.arrayBuffer, 'consume as array buffer', function() { var r = factory(body) return r .arrayBuffer() .then(readArrayBufferAsText) .then(function(text) { assert.equal(text, expected) }) }) }) }) }) } // https://fetch.spec.whatwg.org/#headers-class suite('Headers', function() { test('constructor copies headers', function() { var original = new Headers() original.append('Accept', 'application/json') original.append('Accept', 'text/plain') original.append('Content-Type', 'text/html') var headers = new Headers(original) assert.equal(headers.get('Accept'), 'application/json, text/plain') assert.equal(headers.get('Content-type'), 'text/html') }) test('constructor works with arrays', function() { var array = [['Content-Type', 'text/xml'], ['Breaking-Bad', '<3']] var headers = new Headers(array) assert.equal(headers.get('Content-Type'), 'text/xml') assert.equal(headers.get('Breaking-Bad'), '<3') }) test('headers are case insensitive', function() { var headers = new Headers({Accept: 'application/json'}) assert.equal(headers.get('ACCEPT'), 'application/json') assert.equal(headers.get('Accept'), 'application/json') assert.equal(headers.get('accept'), 'application/json') }) test('appends to existing', function() { var headers = new Headers({Accept: 'application/json'}) assert.isFalse(headers.has('Content-Type')) headers.append('Content-Type', 'application/json') assert.isTrue(headers.has('Content-Type')) assert.equal(headers.get('Content-Type'), 'application/json') }) test('appends values to existing header name', function() { var headers = new Headers({Accept: 'application/json'}) headers.append('Accept', 'text/plain') assert.equal(headers.get('Accept'), 'application/json, text/plain') }) test('sets header name and value', function() { var headers = new Headers() headers.set('Content-Type', 'application/json') assert.equal(headers.get('Content-Type'), 'application/json') }) test('returns null on no header found', function() { var headers = new Headers() assert.isNull(headers.get('Content-Type')) }) test('has headers that are set', function() { var headers = new Headers() headers.set('Content-Type', 'application/json') assert.isTrue(headers.has('Content-Type')) }) test('deletes headers', function() { var headers = new Headers() headers.set('Content-Type', 'application/json') assert.isTrue(headers.has('Content-Type')) headers.delete('Content-Type') assert.isFalse(headers.has('Content-Type')) assert.isNull(headers.get('Content-Type')) }) test('converts field name to string on set and get', function() { var headers = new Headers() headers.set(1, 'application/json') assert.isTrue(headers.has('1')) assert.equal(headers.get(1), 'application/json') }) test('converts field value to string on set and get', function() { var headers = new Headers() headers.set('Content-Type', 1) headers.set('X-CSRF-Token', undefined) assert.equal(headers.get('Content-Type'), '1') assert.equal(headers.get('X-CSRF-Token'), 'undefined') }) test('throws TypeError on invalid character in field name', function() { assert.throws(function() { new Headers({'': 'application/json'}) }, TypeError) assert.throws(function() { new Headers({'Accept:': 'application/json'}) }, TypeError) assert.throws(function() { var headers = new Headers() headers.set({field: 'value'}, 'application/json') }, TypeError) }) featureDependent(test, !brokenFF, 'is iterable with forEach', function() { var headers = new Headers() headers.append('Accept', 'application/json') headers.append('Accept', 'text/plain') headers.append('Content-Type', 'text/html') var results = [] headers.forEach(function(value, key, object) { results.push({value: value, key: key, object: object}) }) assert.equal(results.length, 2) assert.deepEqual({key: 'accept', value: 'application/json, text/plain', object: headers}, results[0]) assert.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1]) }) test('forEach accepts second thisArg argument', function() { var headers = new Headers({Accept: 'application/json'}) var thisArg = 42 headers.forEach(function() { assert.equal(this, thisArg) }, thisArg) }) featureDependent(test, !brokenFF, 'is iterable with keys', function() { var headers = new Headers() headers.append('Accept', 'application/json') headers.append('Accept', 'text/plain') headers.append('Content-Type', 'text/html') var iterator = headers.keys() assert.deepEqual({done: false, value: 'accept'}, iterator.next()) assert.deepEqual({done: false, value: 'content-type'}, iterator.next()) assert.deepEqual({done: true, value: undefined}, iterator.next()) }) featureDependent(test, !brokenFF, 'is iterable with values', function() { var headers = new Headers() headers.append('Accept', 'application/json') headers.append('Accept', 'text/plain') headers.append('Content-Type', 'text/html') var iterator = headers.values() assert.deepEqual({done: false, value: 'application/json, text/plain'}, iterator.next()) assert.deepEqual({done: false, value: 'text/html'}, iterator.next()) assert.deepEqual({done: true, value: undefined}, iterator.next()) }) featureDependent(test, !brokenFF, 'is iterable with entries', function() { var headers = new Headers() headers.append('Accept', 'application/json') headers.append('Accept', 'text/plain') headers.append('Content-Type', 'text/html') var iterator = headers.entries() assert.deepEqual({done: false, value: ['accept', 'application/json, text/plain']}, iterator.next()) assert.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next()) assert.deepEqual({done: true, value: undefined}, iterator.next()) }) }) // https://fetch.spec.whatwg.org/#request-class suite('Request', function() { test('construct with string url', function() { var request = new Request('https://fetch.spec.whatwg.org/') assert.equal(request.url, 'https://fetch.spec.whatwg.org/') }) featureDependent(test, support.url, 'construct with URL instance', function() { var url = new URL('https://fetch.spec.whatwg.org/') url.pathname = 'cors' var request = new Request(url) assert.equal(request.url, 'https://fetch.spec.whatwg.org/cors') }) test('construct with non-Request object', function() { var url = { toString: function() { return 'https://fetch.spec.whatwg.org/' } } var request = new Request(url) assert.equal(request.url, 'https://fetch.spec.whatwg.org/') }) test('construct with Request', function() { var request1 = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out', headers: { accept: 'application/json', 'Content-Type': 'text/plain' } }) var request2 = new Request(request1) return request2.text().then(function(body2) { assert.equal(body2, 'I work out') assert.equal(request2.method, 'POST') assert.equal(request2.url, 'https://fetch.spec.whatwg.org/') assert.equal(request2.headers.get('accept'), 'application/json') assert.equal(request2.headers.get('content-type'), 'text/plain') return request1.text().then( function() { assert(false, 'original request body should have been consumed') }, function(error) { assert(error instanceof TypeError, 'expected TypeError for already read body') } ) }) }) test('construct with Request and override headers', function() { var request1 = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out', headers: { accept: 'application/json', 'X-Request-ID': '123' } }) var request2 = new Request(request1, { headers: {'x-test': '42'} }) assert.equal(request2.headers.get('accept'), undefined) assert.equal(request2.headers.get('x-request-id'), undefined) assert.equal(request2.headers.get('x-test'), '42') }) test('construct with Request and override body', function() { var request1 = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out', headers: { 'Content-Type': 'text/plain' } }) var request2 = new Request(request1, { body: '{"wiggles": 5}', headers: {'Content-Type': 'application/json'} }) return request2.json().then(function(data) { assert.equal(data.wiggles, 5) assert.equal(request2.headers.get('content-type'), 'application/json') }) }) featureDependent(test, !nativeChrome, 'construct with used Request body', function() { var request1 = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out' }) return request1.text().then(function() { assert.throws(function() { new Request(request1) }, TypeError) }) }) test('GET should not have implicit Content-Type', function() { var req = new Request('https://fetch.spec.whatwg.org/') assert.equal(req.headers.get('content-type'), undefined) }) test('POST with blank body should not have implicit Content-Type', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post' }) assert.equal(req.headers.get('content-type'), undefined) }) test('construct with string body sets Content-Type header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out' }) assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8') }) featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: new Blob(['test'], {type: 'image/png'}) }) assert.equal(req.headers.get('content-type'), 'image/png') }) test('construct with body and explicit header uses header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', headers: {'Content-Type': 'image/png'}, body: 'I work out' }) assert.equal(req.headers.get('content-type'), 'image/png') }) featureDependent(test, support.blob, 'construct with Blob body and explicit Content-Type header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', headers: {'Content-Type': 'image/png'}, body: new Blob(['test'], {type: 'text/plain'}) }) assert.equal(req.headers.get('content-type'), 'image/png') }) featureDependent(test, !IEorEdge, 'construct with URLSearchParams body sets Content-Type header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: new URLSearchParams('a=1&b=2') }) assert.equal(req.headers.get('content-type'), 'application/x-www-form-urlencoded;charset=UTF-8') }) featureDependent( test, !IEorEdge, 'construct with URLSearchParams body and explicit Content-Type header', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', headers: {'Content-Type': 'image/png'}, body: new URLSearchParams('a=1&b=2') }) assert.equal(req.headers.get('content-type'), 'image/png') } ) test('construct with unsupported body type', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: {} }) assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8') return req.text().then(function(bodyText) { assert.equal(bodyText, '[object Object]') }) }) test('construct with null body', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post' }) assert.isNull(req.headers.get('content-type')) return req.text().then(function(bodyText) { assert.equal(bodyText, '') }) }) test('clone GET request', function() { var req = new Request('https://fetch.spec.whatwg.org/', { headers: {'content-type': 'text/plain'} }) var clone = req.clone() assert.equal(clone.url, req.url) assert.equal(clone.method, 'GET') assert.equal(clone.headers.get('content-type'), 'text/plain') assert.notEqual(clone.headers, req.headers) assert.isFalse(req.bodyUsed) }) test('clone POST request', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', headers: {'content-type': 'text/plain'}, body: 'I work out' }) var clone = req.clone() assert.equal(clone.method, 'POST') assert.equal(clone.headers.get('content-type'), 'text/plain') assert.notEqual(clone.headers, req.headers) assert.equal(req.bodyUsed, false) return Promise.all([clone.text(), req.clone().text()]).then(function(bodies) { assert.deepEqual(bodies, ['I work out', 'I work out']) }) }) featureDependent(test, !nativeChrome, 'clone with used Request body', function() { var req = new Request('https://fetch.spec.whatwg.org/', { method: 'post', body: 'I work out' }) return req.text().then(function() { assert.throws(function() { req.clone() }, TypeError) }) }) testBodyExtract(function(body) { return new Request('', {method: 'POST', body: body}) }) featureDependent(test, !omitSafari, 'credentials defaults to same-origin', function() { var request = new Request('') assert.equal(request.credentials, 'same-origin') }) test('credentials is overridable', function() { var request = new Request('', {credentials: 'omit'}) assert.equal(request.credentials, 'omit') }) }) // https://fetch.spec.whatwg.org/#response-class suite('Response', function() { test('default status is 200 OK', function() { var res = new Response() assert.equal(res.status, 200) assert.equal(res.statusText, 'OK') assert.isTrue(res.ok) }) test('default status is 200 OK when an explicit undefined status code is passed', function() { var res = new Response('', {status: undefined}) assert.equal(res.status, 200) assert.equal(res.statusText, 'OK') assert.isTrue(res.ok) }) testBodyExtract(function(body) { return new Response(body) }) test('creates Headers object from raw headers', function() { var r = new Response('{"foo":"bar"}', {headers: {'content-type': 'application/json'}}) assert.equal(r.headers instanceof Headers, true) return r.json().then(function(json) { assert.equal(json.foo, 'bar') return json }) }) test('always creates a new Headers instance', function() { var headers = new Headers({'x-hello': 'world'}) var res = new Response('', {headers: headers}) assert.equal(res.headers.get('x-hello'), 'world') assert.notEqual(res.headers, headers) }) test('clone text response', function() { var res = new Response('{"foo":"bar"}', { headers: {'content-type': 'application/json'} }) var clone = res.clone() assert.notEqual(clone.headers, res.headers, 'headers were cloned') assert.equal(clone.headers.get('content-type'), 'application/json') return Promise.all([clone.json(), res.json()]).then(function(jsons) { assert.deepEqual(jsons[0], jsons[1], 'json of cloned object is the same as original') }) }) featureDependent(test, support.blob, 'clone blob response', function() { var req = new Request(new Blob(['test'])) req.clone() assert.equal(req.bodyUsed, false) }) test('error creates error Response', function() { var r = Response.error() assert(r instanceof Response) assert.equal(r.status, 0) assert.equal(r.statusText, '') assert.equal(r.type, 'error') }) test('redirect creates redirect Response', function() { var r = Response.redirect('https://fetch.spec.whatwg.org/', 301) assert(r instanceof Response) assert.equal(r.status, 301) assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/') }) test('construct with string body sets Content-Type header', function() { var r = new Response('I work out') assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8') }) featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() { var r = new Response(new Blob(['test'], {type: 'text/plain'})) assert.equal(r.headers.get('content-type'), 'text/plain') }) test('construct with body and explicit header uses header', function() { var r = new Response('I work out', { headers: { 'Content-Type': 'text/plain' } }) assert.equal(r.headers.get('content-type'), 'text/plain') }) test('init object as first argument', function() { var r = new Response({ status: 201, headers: { 'Content-Type': 'text/html' } }) assert.equal(r.status, 200) assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8') return r.text().then(function(bodyText) { assert.equal(bodyText, '[object Object]') }) }) test('null as first argument', function() { var r = new Response(null) assert.isNull(r.headers.get('content-type')) return r.text().then(function(bodyText) { assert.equal(bodyText, '') }) }) }) // https://fetch.spec.whatwg.org/#body-mixin suite('Body mixin', function() { featureDependent(suite, support.blob, 'arrayBuffer', function() { test('resolves arrayBuffer promise', function() { return fetch('/hello') .then(function(response) { return response.arrayBuffer() }) .then(function(buf) { assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') assert.equal(buf.byteLength, 2) }) }) test('arrayBuffer handles binary data', function() { return fetch('/binary') .then(function(response) { return response.arrayBuffer() }) .then(function(buf) { assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') assert.equal(buf.byteLength, 256, 'buf.byteLength is correct') var view = new Uint8Array(buf) for (var i = 0; i < 256; i++) { assert.equal(view[i], i) } }) }) test('arrayBuffer handles utf-8 data', function() { return fetch('/hello/utf8') .then(function(response) { return response.arrayBuffer() }) .then(function(buf) { assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') assert.equal(buf.byteLength, 5, 'buf.byteLength is correct') var octets = Array.prototype.slice.call(new Uint8Array(buf)) assert.deepEqual(octets, [104, 101, 108, 108, 111]) }) }) test('arrayBuffer handles utf-16le data', function() { return fetch('/hello/utf16le') .then(function(response) { return response.arrayBuffer() }) .then(function(buf) { assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') assert.equal(buf.byteLength, 10, 'buf.byteLength is correct') var octets = Array.prototype.slice.call(new Uint8Array(buf)) assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0]) }) }) test('rejects arrayBuffer promise after body is consumed', function() { return fetch('/hello') .then(function(response) { assert.equal(response.bodyUsed, false) response.blob() assert.equal(response.bodyUsed, true) return response.arrayBuffer() }) .catch(function(error) { assert(error instanceof TypeError, 'Promise rejected after body consumed') }) }) }) featureDependent(suite, support.blob, 'blob', function() { test('resolves blob promise', function() { return fetch('/hello') .then(function(response) { return response.blob() }) .then(function(blob) { assert(blob instanceof Blob, 'blob is a Blob instance') assert.equal(blob.size, 2) }) }) test('blob handles binary data', function() { return fetch('/binary') .then(function(response) { return response.blob() }) .then(function(blob) { assert(blob instanceof Blob, 'blob is a Blob instance') assert.equal(blob.size, 256, 'blob.size is correct') }) }) test('blob handles utf-8 data', function() { return fetch('/hello/utf8') .then(function(response) { return response.blob() }) .then(readBlobAsBytes) .then(function(octets) { assert.equal(octets.length, 5, 'blob.size is correct') assert.deepEqual(octets, [104, 101, 108, 108, 111]) }) }) test('blob handles utf-16le data', function() { return fetch('/hello/utf16le') .then(function(response) { return response.blob() }) .then(readBlobAsBytes) .then(function(octets) { assert.equal(octets.length, 10, 'blob.size is correct') assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0]) }) }) test('rejects blob promise after body is consumed', function() { return fetch('/hello') .then(function(response) { assert(response.blob, 'Body does not implement blob') assert.equal(response.bodyUsed, false) response.text() assert.equal(response.bodyUsed, true) return response.blob() }) .catch(function(error) { assert(error instanceof TypeError, 'Promise rejected after body consumed') }) }) }) featureDependent(suite, support.formData, 'formData', function() { test('post sets content-type header', function() { return fetch('/request', { method: 'post', body: new FormData() }) .then(function(response) { return response.json() }) .then(function(json) { assert.equal(json.method, 'POST') assert(/^multipart\/form-data;/.test(json.headers['content-type'])) }) }) featureDependent(test, !nativeChrome && !nativeEdge, 'formData rejects after body was consumed', function() { return fetch('/json') .then(function(response) { assert(response.formData, 'Body does not implement formData') response.formData() return response.formData() }) .catch(function(error) { if (error instanceof chai.AssertionError) { throw error } else { assert(error instanceof TypeError, 'Promise rejected after body consumed') } }) }) featureDependent( test, !nativeChrome && !nativeSafari && !nativeEdge, 'parses form encoded response', function() { return fetch('/form') .then(function(response) { return response.formData() }) .then(function(form) { assert(form instanceof FormData, 'Parsed a FormData object') }) } ) }) suite('json', function() { test('parses json response', function() { return fetch('/json') .then(function(response) { return response.json() }) .then(function(json) { assert.equal(json.name, 'Hubot') assert.equal(json.login, 'hubot') }) }) test('rejects json promise after body is consumed', function() { return fetch('/json') .then(function(response) { assert(response.json, 'Body does not implement json') assert.equal(response.bodyUsed, false) response.text() assert.equal(response.bodyUsed, true) return response.json() }) .catch(function(error) { assert(error instanceof TypeError, 'Promise rejected after body consumed') }) }) featureDependent(test, !polyfillFirefox, 'handles json parse error', function() { return fetch('/json-error') .then(function(response) { return response.json() }) .catch(function(error) { if (!IEorEdge) assert(error instanceof Error, 'JSON exception is an Error instance') assert(error.message, 'JSON exception has an error message') }) }) }) suite('text', function() { test('handles 204 No Content response', function() { return fetch('/empty') .then(function(response) { assert.equal(response.status, 204) return response.text() }) .then(function(body) { assert.equal(body, '') }) }) test('resolves text promise', function() { return fetch('/hello') .then(function(response) { return response.text() }) .then(function(text) { assert.equal(text, 'hi') }) }) test('rejects text promise after body is consumed', function() { return fetch('/hello') .then(function(response) { assert(response.text, 'Body does not implement text') assert.equal(response.bodyUsed, false) response.text() assert.equal(response.bodyUsed, true) return response.text() }) .catch(function(error) { assert(error instanceof TypeError, 'Promise rejected after body consumed') }) }) }) }) suite('fetch method', function() { suite('promise resolution', function() { test('resolves promise on 500 error', function() { return fetch('/boom') .then(function(response) { assert.equal(response.status, 500) assert.equal(response.ok, false) return response.text() }) .then(function(body) { assert.equal(body, 'boom') }) }) test.skip('rejects promise for network error', function() { return fetch('/error') .then(function(response) { assert(false, 'HTTP status ' + response.status + ' was treated as success') }) .catch(function(error) { assert(error instanceof TypeError, 'Rejected with Error') }) }) test('rejects when Request constructor throws', function() { return fetch('/request', {method: 'GET', body: 'invalid'}) .then(function() { assert(false, 'Invalid Request init was accepted') }) .catch(function(error) { assert(error instanceof TypeError, 'Rejected with Error') }) }) }) suite('request', function() { test('sends headers', function() { return fetch('/request', { headers: { Accept: 'application/json', 'X-Test': '42' } }) .then(function(response) { return response.json() }) .then(function(json) { assert.equal(json.headers['accept'], 'application/json') assert.equal(json.headers['x-test'], '42') }) }) test('with Request as argument', function() { var request = new Request('/request', { headers: { Accept: 'application/json', 'X-Test': '42' } }) return fetch(request) .then(function(response) { return response.json() }) .then(function(json) { assert.equal(json.headers['accept'], 'application/json') assert.equal(json.headers['x-test'], '42') }) }) test('reusing same Request multiple times', function() { var request = new Request('/request', { headers: { Accept: 'application/json', 'X-Test': '42' } }) var responses = [] return fetch(request) .then(function(response) { responses.push(response) return fetch(request) }) .then(function(response) { responses.push(response) return fetch(request) }) .then(function(response) { responses.push(response) return Promise.all( responses.map(function(r) { return r.json() }) ) }) .then(function(jsons) { jsons.forEach(function(json) { assert.equal(json.headers['accept'], 'application/json') assert.equal(json.headers['x-test'], '42') }) }) }) featureDependent(suite, support.arrayBuffer, 'ArrayBuffer', function() { test('ArrayBuffer body', function() { return fetch('/request', { method: 'post', body: arrayBufferFromText('name=Hubot') }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'POST') assert.equal(request.data, 'name=Hubot') }) }) test('DataView body', function() { return fetch('/request', { method: 'post', body: new DataView(arrayBufferFromText('name=Hubot')) }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'POST') assert.equal(request.data, 'name=Hubot') }) }) test('TypedArray body', function() { return fetch('/request', { method: 'post', body: new Uint8Array(arrayBufferFromText('name=Hubot')) }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'POST') assert.equal(request.data, 'name=Hubot') }) }) }) featureDependent(test, !IEorEdge, 'sends URLSearchParams body', function() { return fetch('/request', { method: 'post', body: new URLSearchParams('a=1&b=2') }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'POST') assert.equal(request.data, 'a=1&b=2') }) }) }) featureDependent(suite, exerciseMode !== 'native' || support.aborting, 'aborting', function() { test('initially aborted signal', function() { var controller = new AbortController() controller.abort() return fetch('/request', { signal: controller.signal }).then( function() { assert.ok(false) }, function(error) { if (!IEorEdge) assert.instanceOf(error, WHATWGFetch.DOMException) assert.equal(error.name, 'AbortError') } ) }) test('initially aborted signal within Request', function() { var controller = new AbortController() controller.abort() var request = new Request('/request', {signal: controller.signal}) return fetch(request).then( function() { assert.ok(false) }, function(error) { assert.equal(error.name, 'AbortError') } ) }) test('mid-request', function() { var controller = new AbortController() setTimeout(function() { controller.abort() }, 30) return fetch('/slow?_=' + new Date().getTime(), { signal: controller.signal }).then( function() { assert.ok(false) }, function(error) { assert.equal(error.name, 'AbortError') } ) }) test('mid-request within Request', function() { var controller = new AbortController() var request = new Request('/slow?_=' + new Date().getTime(), {signal: controller.signal}) setTimeout(function() { controller.abort() }, 30) return fetch(request).then( function() { assert.ok(false) }, function(error) { assert.equal(error.name, 'AbortError') } ) }) test('abort multiple with same signal', function() { var controller = new AbortController() setTimeout(function() { controller.abort() }, 30) return Promise.all([ fetch('/slow?_=' + new Date().getTime(), { signal: controller.signal }).then( function() { assert.ok(false) }, function(error) { assert.equal(error.name, 'AbortError') } ), fetch('/slow?_=' + new Date().getTime(), { signal: controller.signal }).then( function() { assert.ok(false) }, function(error) { assert.equal(error.name, 'AbortError') } ) ]) }) }) suite('response', function() { test('populates body', function() { return fetch('/hello') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) test('parses headers', function() { return fetch('/headers?' + new Date().getTime()).then(function(response) { assert.equal(response.headers.get('Date'), 'Mon, 13 Oct 2014 21:02:27 GMT') assert.equal(response.headers.get('Content-Type'), 'text/html; charset=utf-8') }) }) }) // https://fetch.spec.whatwg.org/#methods suite('HTTP methods', function() { test('supports HTTP GET', function() { return fetch('/request', { method: 'get' }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'GET') assert.equal(request.data, '') }) }) test('GET with body throws TypeError', function() { assert.throw(function() { new Request('', { method: 'get', body: 'invalid' }) }, TypeError) }) test('HEAD with body throws TypeError', function() { assert.throw(function() { new Request('', { method: 'head', body: 'invalid' }) }, TypeError) }) test('supports HTTP POST', function() { return fetch('/request', { method: 'post', body: 'name=Hubot' }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'POST') assert.equal(request.data, 'name=Hubot') }) }) test('supports HTTP PUT', function() { return fetch('/request', { method: 'put', body: 'name=Hubot' }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'PUT') assert.equal(request.data, 'name=Hubot') }) }) test('supports HTTP PATCH', function() { return fetch('/request', { method: 'PATCH', body: 'name=Hubot' }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'PATCH') assert.equal(request.data, 'name=Hubot') }) }) test('supports HTTP DELETE', function() { return fetch('/request', { method: 'delete' }) .then(function(response) { return response.json() }) .then(function(request) { assert.equal(request.method, 'DELETE') assert.equal(request.data, '') }) }) }) // https://fetch.spec.whatwg.org/#atomic-http-redirect-handling suite('Atomic HTTP redirect handling', function() { test('handles 301 redirect response', function() { return fetch('/redirect/301') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) assert.match(response.url, /\/hello/) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) test('handles 302 redirect response', function() { return fetch('/redirect/302') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) assert.match(response.url, /\/hello/) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) test('handles 303 redirect response', function() { return fetch('/redirect/303') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) assert.match(response.url, /\/hello/) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) test('handles 307 redirect response', function() { return fetch('/redirect/307') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) assert.match(response.url, /\/hello/) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) featureDependent(test, support.permanentRedirect, 'handles 308 redirect response', function() { return fetch('/redirect/308') .then(function(response) { assert.equal(response.status, 200) assert.equal(response.ok, true) assert.match(response.url, /\/hello/) return response.text() }) .then(function(body) { assert.equal(body, 'hi') }) }) }) // https://fetch.spec.whatwg.org/#concept-request-credentials-mode suite('credentials mode', function() { setup(function() { return fetch('/cookie?name=foo&value=reset', {credentials: 'same-origin'}) }) featureDependent(suite, exerciseMode === 'native', 'omit', function() { test('does not accept cookies with omit credentials', function() { return fetch('/cookie?name=foo&value=bar', {credentials: 'omit'}) .then(function() { return fetch('/cookie?name=foo', {credentials: 'same-origin'}) }) .then(function(response) { return response.text() }) .then(function(data) { assert.equal(data, 'reset') }) }) test('does not send cookies with omit credentials', function() { return fetch('/cookie?name=foo&value=bar') .then(function() { return fetch('/cookie?name=foo', {credentials: 'omit'}) }) .then(function(response) { return response.text() }) .then(function(data) { assert.equal(data, '') }) }) }) suite('same-origin', function() { test('send cookies with same-origin credentials', function() { return fetch('/cookie?name=foo&value=bar', {credentials: 'same-origin'}) .then(function() { return fetch('/cookie?name=foo', {credentials: 'same-origin'}) }) .then(function(response) { return response.text() }) .then(function(data) { assert.equal(data, 'bar') }) }) }) suite('include', function() { test('send cookies with include credentials', function() { return fetch('/cookie?name=foo&value=bar', {credentials: 'include'}) .then(function() { return fetch('/cookie?name=foo', {credentials: 'include'}) }) .then(function(response) { return response.text() }) .then(function(data) { assert.equal(data, 'bar') }) }) }) }) }) }) }) fetch-3.0.0/test/worker-adapter.js000066400000000000000000000011471334447003400170540ustar00rootroot00000000000000/* global Mocha */ var mochaRun = mocha.run mocha.run = function() {} mocha.suite.suites.unshift(Mocha.Suite.create(mocha.suite, 'worker')) var worker = new Worker('/base/test/worker.js') worker.addEventListener('message', function(e) { switch (e.data.name) { case 'pass': test(e.data.title, function() {}) break case 'pending': test(e.data.title) break case 'fail': test(e.data.title, function() { var err = new Error(e.data.message) err.stack = e.data.stack throw err }) break case 'end': mochaRun() break } }) fetch-3.0.0/test/worker.js000066400000000000000000000016161334447003400154370ustar00rootroot00000000000000importScripts('/base/node_modules/mocha/mocha.js') importScripts('/base/node_modules/chai/chai.js') mocha.setup('tdd') self.assert = chai.assert importScripts('/base/node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js') importScripts('/base/dist/fetch.umd.js') importScripts('/base/test/test.js') function title(test) { return test.fullTitle().replace(/#/g, '') } function reporter(runner) { runner.on('pending', function(test) { self.postMessage({name: 'pending', title: title(test)}) }) runner.on('pass', function(test) { self.postMessage({name: 'pass', title: title(test)}) }) runner.on('fail', function(test, err) { self.postMessage({ name: 'fail', title: title(test), message: err.message, stack: err.stack }) }) runner.on('end', function() { self.postMessage({name: 'end'}) }) } mocha.reporter(reporter).run()