UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

2,599 lines 91.5 kB
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global.XHRMock = factory());
}(this, (function () { 'use strict';

var MockResponse = /** @class */ (function () {
    function MockResponse() {
        this._status = 200;
        this._reason = 'OK';
        this._headers = {};
        this._body = null;
    }
    MockResponse.prototype.status = function (status) {
        if (typeof status !== 'undefined') {
            this._status = status;
            return this;
        }
        else {
            return this._status;
        }
    };
    MockResponse.prototype.reason = function (reason) {
        if (typeof reason !== 'undefined') {
            this._reason = reason;
            return this;
        }
        else {
            return this._reason;
        }
    };
    MockResponse.prototype.statusText = function (reason) {
        console.warn('xhr-mock: MockResponse.statusText() has been deprecated. Use MockResponse.reason() instead.');
        if (typeof reason !== 'undefined') {
            return this.reason(reason);
        }
        else {
            return this.reason();
        }
    };
    MockResponse.prototype.header = function (name, value) {
        if (typeof value !== 'undefined') {
            this._headers[name.toLowerCase()] = value;
            return this;
        }
        else {
            return this._headers[name.toLowerCase()] || null;
        }
    };
    MockResponse.prototype.headers = function (headers) {
        if (typeof headers === 'object') {
            for (var name in headers) {
                if (headers.hasOwnProperty(name)) {
                    this.header(name, headers[name]);
                }
            }
            return this;
        }
        else {
            return this._headers;
        }
    };
    MockResponse.prototype.body = function (body) {
        if (typeof body !== 'undefined') {
            this._body = body;
            return this;
        }
        else {
            return this._body;
        }
    };
    return MockResponse;
}());

function createResponseFromObject(object) {
    var status = object.status, reason = object.reason, headers = object.headers, body = object.body;
    var response = new MockResponse();
    if (status) {
        response.status(status);
    }
    if (reason) {
        response.reason(reason);
    }
    if (headers) {
        response.headers(headers);
    }
    if (body) {
        response.body(body);
    }
    return response;
}

function createMockFunction (method, url, mock) {
    var matches = function (req) {
        var requestMethod = req.method();
        var requestURL = req.url().toString();
        if (requestMethod.toUpperCase() !== method.toUpperCase()) {
            return false;
        }
        if (url instanceof RegExp) {
            url.lastIndex = 0; //reset state of global regexp
            return url.test(requestURL);
        }
        return requestURL === url; //TODO: should we use .startsWith()???
    };
    return function (req, res) {
        if (matches(req)) {
            if (typeof mock === 'object') {
                return createResponseFromObject(mock);
            }
            else {
                return mock(req, res);
            }
        }
    };
}

/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0

THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */

var extendStatics = function(d, b) {
    extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return extendStatics(d, b);
};

function __extends(d, b) {
    extendStatics(d, b);
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

var __assign = function() {
    __assign = Object.assign || function __assign(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};









function __awaiter(thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
}

function __generator(thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
}

var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};





function createCommonjsModule(fn, module) {
	return module = { exports: {} }, fn(module, module.exports), module.exports;
}

var punycode = createCommonjsModule(function (module, exports) {
/*! https://mths.be/punycode v1.3.2 by @mathias */
(function(root) {

	/** Detect free variables */
	var freeExports = 'object' == 'object' && exports &&
		!exports.nodeType && exports;
	var freeModule = 'object' == 'object' && module &&
		!module.nodeType && module;
	var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
	if (
		freeGlobal.global === freeGlobal ||
		freeGlobal.window === freeGlobal ||
		freeGlobal.self === freeGlobal
	) {
		root = freeGlobal;
	}

	/**
	 * The `punycode` object.
	 * @name punycode
	 * @type Object
	 */
	var punycode,

	/** Highest positive signed 32-bit float value */
	maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1

	/** Bootstring parameters */
	base = 36,
	tMin = 1,
	tMax = 26,
	skew = 38,
	damp = 700,
	initialBias = 72,
	initialN = 128, // 0x80
	delimiter = '-', // '\x2D'

	/** Regular expressions */
	regexPunycode = /^xn--/,
	regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
	regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators

	/** Error messages */
	errors = {
		'overflow': 'Overflow: input needs wider integers to process',
		'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
		'invalid-input': 'Invalid input'
	},

	/** Convenience shortcuts */
	baseMinusTMin = base - tMin,
	floor = Math.floor,
	stringFromCharCode = String.fromCharCode,

	/** Temporary variable */
	key;

	/*--------------------------------------------------------------------------*/

	/**
	 * A generic error utility function.
	 * @private
	 * @param {String} type The error type.
	 * @returns {Error} Throws a `RangeError` with the applicable error message.
	 */
	function error(type) {
		throw RangeError(errors[type]);
	}

	/**
	 * A generic `Array#map` utility function.
	 * @private
	 * @param {Array} array The array to iterate over.
	 * @param {Function} callback The function that gets called for every array
	 * item.
	 * @returns {Array} A new array of values returned by the callback function.
	 */
	function map(array, fn) {
		var length = array.length;
		var result = [];
		while (length--) {
			result[length] = fn(array[length]);
		}
		return result;
	}

	/**
	 * A simple `Array#map`-like wrapper to work with domain name strings or email
	 * addresses.
	 * @private
	 * @param {String} domain The domain name or email address.
	 * @param {Function} callback The function that gets called for every
	 * character.
	 * @returns {Array} A new string of characters returned by the callback
	 * function.
	 */
	function mapDomain(string, fn) {
		var parts = string.split('@');
		var result = '';
		if (parts.length > 1) {
			// In email addresses, only the domain name should be punycoded. Leave
			// the local part (i.e. everything up to `@`) intact.
			result = parts[0] + '@';
			string = parts[1];
		}
		// Avoid `split(regex)` for IE8 compatibility. See #17.
		string = string.replace(regexSeparators, '\x2E');
		var labels = string.split('.');
		var encoded = map(labels, fn).join('.');
		return result + encoded;
	}

	/**
	 * Creates an array containing the numeric code points of each Unicode
	 * character in the string. While JavaScript uses UCS-2 internally,
	 * this function will convert a pair of surrogate halves (each of which
	 * UCS-2 exposes as separate characters) into a single code point,
	 * matching UTF-16.
	 * @see `punycode.ucs2.encode`
	 * @see <https://mathiasbynens.be/notes/javascript-encoding>
	 * @memberOf punycode.ucs2
	 * @name decode
	 * @param {String} string The Unicode input string (UCS-2).
	 * @returns {Array} The new array of code points.
	 */
	function ucs2decode(string) {
		var output = [],
		    counter = 0,
		    length = string.length,
		    value,
		    extra;
		while (counter < length) {
			value = string.charCodeAt(counter++);
			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
				// high surrogate, and there is a next character
				extra = string.charCodeAt(counter++);
				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
				} else {
					// unmatched surrogate; only append this code unit, in case the next
					// code unit is the high surrogate of a surrogate pair
					output.push(value);
					counter--;
				}
			} else {
				output.push(value);
			}
		}
		return output;
	}

	/**
	 * Creates a string based on an array of numeric code points.
	 * @see `punycode.ucs2.decode`
	 * @memberOf punycode.ucs2
	 * @name encode
	 * @param {Array} codePoints The array of numeric code points.
	 * @returns {String} The new Unicode string (UCS-2).
	 */
	function ucs2encode(array) {
		return map(array, function(value) {
			var output = '';
			if (value > 0xFFFF) {
				value -= 0x10000;
				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
				value = 0xDC00 | value & 0x3FF;
			}
			output += stringFromCharCode(value);
			return output;
		}).join('');
	}

	/**
	 * Converts a basic code point into a digit/integer.
	 * @see `digitToBasic()`
	 * @private
	 * @param {Number} codePoint The basic numeric code point value.
	 * @returns {Number} The numeric value of a basic code point (for use in
	 * representing integers) in the range `0` to `base - 1`, or `base` if
	 * the code point does not represent a value.
	 */
	function basicToDigit(codePoint) {
		if (codePoint - 48 < 10) {
			return codePoint - 22;
		}
		if (codePoint - 65 < 26) {
			return codePoint - 65;
		}
		if (codePoint - 97 < 26) {
			return codePoint - 97;
		}
		return base;
	}

	/**
	 * Converts a digit/integer into a basic code point.
	 * @see `basicToDigit()`
	 * @private
	 * @param {Number} digit The numeric value of a basic code point.
	 * @returns {Number} The basic code point whose value (when used for
	 * representing integers) is `digit`, which needs to be in the range
	 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
	 * used; else, the lowercase form is used. The behavior is undefined
	 * if `flag` is non-zero and `digit` has no uppercase form.
	 */
	function digitToBasic(digit, flag) {
		//  0..25 map to ASCII a..z or A..Z
		// 26..35 map to ASCII 0..9
		return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
	}

	/**
	 * Bias adaptation function as per section 3.4 of RFC 3492.
	 * http://tools.ietf.org/html/rfc3492#section-3.4
	 * @private
	 */
	function adapt(delta, numPoints, firstTime) {
		var k = 0;
		delta = firstTime ? floor(delta / damp) : delta >> 1;
		delta += floor(delta / numPoints);
		for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
			delta = floor(delta / baseMinusTMin);
		}
		return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
	}

	/**
	 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
	 * symbols.
	 * @memberOf punycode
	 * @param {String} input The Punycode string of ASCII-only symbols.
	 * @returns {String} The resulting string of Unicode symbols.
	 */
	function decode(input) {
		// Don't use UCS-2
		var output = [],
		    inputLength = input.length,
		    out,
		    i = 0,
		    n = initialN,
		    bias = initialBias,
		    basic,
		    j,
		    index,
		    oldi,
		    w,
		    k,
		    digit,
		    t,
		    /** Cached calculation results */
		    baseMinusT;

		// Handle the basic code points: let `basic` be the number of input code
		// points before the last delimiter, or `0` if there is none, then copy
		// the first basic code points to the output.

		basic = input.lastIndexOf(delimiter);
		if (basic < 0) {
			basic = 0;
		}

		for (j = 0; j < basic; ++j) {
			// if it's not a basic code point
			if (input.charCodeAt(j) >= 0x80) {
				error('not-basic');
			}
			output.push(input.charCodeAt(j));
		}

		// Main decoding loop: start just after the last delimiter if any basic code
		// points were copied; start at the beginning otherwise.

		for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {

			// `index` is the index of the next character to be consumed.
			// Decode a generalized variable-length integer into `delta`,
			// which gets added to `i`. The overflow checking is easier
			// if we increase `i` as we go, then subtract off its starting
			// value at the end to obtain `delta`.
			for (oldi = i, w = 1, k = base; /* no condition */; k += base) {

				if (index >= inputLength) {
					error('invalid-input');
				}

				digit = basicToDigit(input.charCodeAt(index++));

				if (digit >= base || digit > floor((maxInt - i) / w)) {
					error('overflow');
				}

				i += digit * w;
				t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);

				if (digit < t) {
					break;
				}

				baseMinusT = base - t;
				if (w > floor(maxInt / baseMinusT)) {
					error('overflow');
				}

				w *= baseMinusT;

			}

			out = output.length + 1;
			bias = adapt(i - oldi, out, oldi == 0);

			// `i` was supposed to wrap around from `out` to `0`,
			// incrementing `n` each time, so we'll fix that now:
			if (floor(i / out) > maxInt - n) {
				error('overflow');
			}

			n += floor(i / out);
			i %= out;

			// Insert `n` at position `i` of the output
			output.splice(i++, 0, n);

		}

		return ucs2encode(output);
	}

	/**
	 * Converts a string of Unicode symbols (e.g. a domain name label) to a
	 * Punycode string of ASCII-only symbols.
	 * @memberOf punycode
	 * @param {String} input The string of Unicode symbols.
	 * @returns {String} The resulting Punycode string of ASCII-only symbols.
	 */
	function encode(input) {
		var n,
		    delta,
		    handledCPCount,
		    basicLength,
		    bias,
		    j,
		    m,
		    q,
		    k,
		    t,
		    currentValue,
		    output = [],
		    /** `inputLength` will hold the number of code points in `input`. */
		    inputLength,
		    /** Cached calculation results */
		    handledCPCountPlusOne,
		    baseMinusT,
		    qMinusT;

		// Convert the input in UCS-2 to Unicode
		input = ucs2decode(input);

		// Cache the length
		inputLength = input.length;

		// Initialize the state
		n = initialN;
		delta = 0;
		bias = initialBias;

		// Handle the basic code points
		for (j = 0; j < inputLength; ++j) {
			currentValue = input[j];
			if (currentValue < 0x80) {
				output.push(stringFromCharCode(currentValue));
			}
		}

		handledCPCount = basicLength = output.length;

		// `handledCPCount` is the number of code points that have been handled;
		// `basicLength` is the number of basic code points.

		// Finish the basic string - if it is not empty - with a delimiter
		if (basicLength) {
			output.push(delimiter);
		}

		// Main encoding loop:
		while (handledCPCount < inputLength) {

			// All non-basic code points < n have been handled already. Find the next
			// larger one:
			for (m = maxInt, j = 0; j < inputLength; ++j) {
				currentValue = input[j];
				if (currentValue >= n && currentValue < m) {
					m = currentValue;
				}
			}

			// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
			// but guard against overflow
			handledCPCountPlusOne = handledCPCount + 1;
			if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
				error('overflow');
			}

			delta += (m - n) * handledCPCountPlusOne;
			n = m;

			for (j = 0; j < inputLength; ++j) {
				currentValue = input[j];

				if (currentValue < n && ++delta > maxInt) {
					error('overflow');
				}

				if (currentValue == n) {
					// Represent delta as a generalized variable-length integer
					for (q = delta, k = base; /* no condition */; k += base) {
						t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
						if (q < t) {
							break;
						}
						qMinusT = q - t;
						baseMinusT = base - t;
						output.push(
							stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
						);
						q = floor(qMinusT / baseMinusT);
					}

					output.push(stringFromCharCode(digitToBasic(q, 0)));
					bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
					delta = 0;
					++handledCPCount;
				}
			}

			++delta;
			++n;

		}
		return output.join('');
	}

	/**
	 * Converts a Punycode string representing a domain name or an email address
	 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
	 * it doesn't matter if you call it on a string that has already been
	 * converted to Unicode.
	 * @memberOf punycode
	 * @param {String} input The Punycoded domain name or email address to
	 * convert to Unicode.
	 * @returns {String} The Unicode representation of the given Punycode
	 * string.
	 */
	function toUnicode(input) {
		return mapDomain(input, function(string) {
			return regexPunycode.test(string)
				? decode(string.slice(4).toLowerCase())
				: string;
		});
	}

	/**
	 * Converts a Unicode string representing a domain name or an email address to
	 * Punycode. Only the non-ASCII parts of the domain name will be converted,
	 * i.e. it doesn't matter if you call it with a domain that's already in
	 * ASCII.
	 * @memberOf punycode
	 * @param {String} input The domain name or email address to convert, as a
	 * Unicode string.
	 * @returns {String} The Punycode representation of the given domain name or
	 * email address.
	 */
	function toASCII(input) {
		return mapDomain(input, function(string) {
			return regexNonASCII.test(string)
				? 'xn--' + encode(string)
				: string;
		});
	}

	/*--------------------------------------------------------------------------*/

	/** Define the public API */
	punycode = {
		/**
		 * A string representing the current Punycode.js version number.
		 * @memberOf punycode
		 * @type String
		 */
		'version': '1.3.2',
		/**
		 * An object of methods to convert from JavaScript's internal character
		 * representation (UCS-2) to Unicode code points, and back.
		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
		 * @memberOf punycode
		 * @type Object
		 */
		'ucs2': {
			'decode': ucs2decode,
			'encode': ucs2encode
		},
		'decode': decode,
		'encode': encode,
		'toASCII': toASCII,
		'toUnicode': toUnicode
	};

	/** Expose `punycode` */
	// Some AMD build optimizers, like r.js, check for specific condition patterns
	// like the following:
	if (
		typeof undefined == 'function' &&
		typeof undefined.amd == 'object' &&
		undefined.amd
	) {
		undefined('punycode', function() {
			return punycode;
		});
	} else if (freeExports && freeModule) {
		if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
			freeModule.exports = punycode;
		} else { // in Narwhal or RingoJS v0.7.0-
			for (key in punycode) {
				punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
			}
		}
	} else { // in Rhino or a web browser
		root.punycode = punycode;
	}

}(commonjsGlobal));
});

var util = {
  isString: function(arg) {
    return typeof(arg) === 'string';
  },
  isObject: function(arg) {
    return typeof(arg) === 'object' && arg !== null;
  },
  isNull: function(arg) {
    return arg === null;
  },
  isNullOrUndefined: function(arg) {
    return arg == null;
  }
};

// Copyright Joyent, Inc. and other Node contributors.
//
// 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.

// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

var decode = function(qs, sep, eq, options) {
  sep = sep || '&';
  eq = eq || '=';
  var obj = {};

  if (typeof qs !== 'string' || qs.length === 0) {
    return obj;
  }

  var regexp = /\+/g;
  qs = qs.split(sep);

  var maxKeys = 1000;
  if (options && typeof options.maxKeys === 'number') {
    maxKeys = options.maxKeys;
  }

  var len = qs.length;
  // maxKeys <= 0 means that we should not limit keys count
  if (maxKeys > 0 && len > maxKeys) {
    len = maxKeys;
  }

  for (var i = 0; i < len; ++i) {
    var x = qs[i].replace(regexp, '%20'),
        idx = x.indexOf(eq),
        kstr, vstr, k, v;

    if (idx >= 0) {
      kstr = x.substr(0, idx);
      vstr = x.substr(idx + 1);
    } else {
      kstr = x;
      vstr = '';
    }

    k = decodeURIComponent(kstr);
    v = decodeURIComponent(vstr);

    if (!hasOwnProperty(obj, k)) {
      obj[k] = v;
    } else if (Array.isArray(obj[k])) {
      obj[k].push(v);
    } else {
      obj[k] = [obj[k], v];
    }
  }

  return obj;
};

// Copyright Joyent, Inc. and other Node contributors.
//
// 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.

var stringifyPrimitive = function(v) {
  switch (typeof v) {
    case 'string':
      return v;

    case 'boolean':
      return v ? 'true' : 'false';

    case 'number':
      return isFinite(v) ? v : '';

    default:
      return '';
  }
};

var encode = function(obj, sep, eq, name) {
  sep = sep || '&';
  eq = eq || '=';
  if (obj === null) {
    obj = undefined;
  }

  if (typeof obj === 'object') {
    return Object.keys(obj).map(function(k) {
      var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
      if (Array.isArray(obj[k])) {
        return obj[k].map(function(v) {
          return ks + encodeURIComponent(stringifyPrimitive(v));
        }).join(sep);
      } else {
        return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
      }
    }).join(sep);

  }

  if (!name) return '';
  return encodeURIComponent(stringifyPrimitive(name)) + eq +
         encodeURIComponent(stringifyPrimitive(obj));
};

var querystring = createCommonjsModule(function (module, exports) {
exports.decode = exports.parse = decode;
exports.encode = exports.stringify = encode;
});

var querystring_1 = querystring.decode;
var querystring_2 = querystring.parse;
var querystring_3 = querystring.encode;
var querystring_4 = querystring.stringify;

var parse = urlParse;
var format = urlFormat;

function Url() {
  this.protocol = null;
  this.slashes = null;
  this.auth = null;
  this.host = null;
  this.port = null;
  this.hostname = null;
  this.hash = null;
  this.search = null;
  this.query = null;
  this.pathname = null;
  this.path = null;
  this.href = null;
}

// Reference: RFC 3986, RFC 1808, RFC 2396

// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i;
var portPattern = /:[0-9]*$/;
var simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/;
var delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'];
var unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims);
var autoEscape = ['\''].concat(unwise);
var nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape);
var hostEndingChars = ['/', '?', '#'];
var hostnameMaxLen = 255;
var hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/;
var hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/;
var unsafeProtocol = {
      'javascript': true,
      'javascript:': true
    };
var hostlessProtocol = {
      'javascript': true,
      'javascript:': true
    };
var slashedProtocol = {
      'http': true,
      'https': true,
      'ftp': true,
      'gopher': true,
      'file': true,
      'http:': true,
      'https:': true,
      'ftp:': true,
      'gopher:': true,
      'file:': true
    };

function urlParse(url, parseQueryString, slashesDenoteHost) {
  if (url && util.isObject(url) && url instanceof Url) return url;

  var u = new Url;
  u.parse(url, parseQueryString, slashesDenoteHost);
  return u;
}

Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
  if (!util.isString(url)) {
    throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
  }

  // Copy chrome, IE, opera backslash-handling behavior.
  // Back slashes before the query string get converted to forward slashes
  // See: https://code.google.com/p/chromium/issues/detail?id=25916
  var queryIndex = url.indexOf('?'),
      splitter =
          (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
      uSplit = url.split(splitter),
      slashRegex = /\\/g;
  uSplit[0] = uSplit[0].replace(slashRegex, '/');
  url = uSplit.join(splitter);

  var rest = url;

  // trim before proceeding.
  // This is to support parse stuff like "  http://foo.com  \n"
  rest = rest.trim();

  if (!slashesDenoteHost && url.split('#').length === 1) {
    // Try fast path regexp
    var simplePath = simplePathPattern.exec(rest);
    if (simplePath) {
      this.path = rest;
      this.href = rest;
      this.pathname = simplePath[1];
      if (simplePath[2]) {
        this.search = simplePath[2];
        if (parseQueryString) {
          this.query = querystring.parse(this.search.substr(1));
        } else {
          this.query = this.search.substr(1);
        }
      } else if (parseQueryString) {
        this.search = '';
        this.query = {};
      }
      return this;
    }
  }

  var proto = protocolPattern.exec(rest);
  if (proto) {
    proto = proto[0];
    var lowerProto = proto.toLowerCase();
    this.protocol = lowerProto;
    rest = rest.substr(proto.length);
  }

  // figure out if it's got a host
  // user@server is *always* interpreted as a hostname, and url
  // resolution will treat //foo/bar as host=foo,path=bar because that's
  // how the browser resolves relative URLs.
  if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
    var slashes = rest.substr(0, 2) === '//';
    if (slashes && !(proto && hostlessProtocol[proto])) {
      rest = rest.substr(2);
      this.slashes = true;
    }
  }

  if (!hostlessProtocol[proto] &&
      (slashes || (proto && !slashedProtocol[proto]))) {

    // there's a hostname.
    // the first instance of /, ?, ;, or # ends the host.
    //
    // If there is an @ in the hostname, then non-host chars *are* allowed
    // to the left of the last @ sign, unless some host-ending character
    // comes *before* the @-sign.
    // URLs are obnoxious.
    //
    // ex:
    // http://a@b@c/ => user:a@b host:c
    // http://a@b?@c => user:a host:c path:/?@c

    // v0.12 TODO(isaacs): This is not quite how Chrome does things.
    // Review our test case against browsers more comprehensively.

    // find the first instance of any hostEndingChars
    var hostEnd = -1;
    for (var i = 0; i < hostEndingChars.length; i++) {
      var hec = rest.indexOf(hostEndingChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }

    // at this point, either we have an explicit point where the
    // auth portion cannot go past, or the last @ char is the decider.
    var auth, atSign;
    if (hostEnd === -1) {
      // atSign can be anywhere.
      atSign = rest.lastIndexOf('@');
    } else {
      // atSign must be in auth portion.
      // http://a@b/c@d => host:b auth:a path:/c@d
      atSign = rest.lastIndexOf('@', hostEnd);
    }

    // Now we have a portion which is definitely the auth.
    // Pull that off.
    if (atSign !== -1) {
      auth = rest.slice(0, atSign);
      rest = rest.slice(atSign + 1);
      this.auth = decodeURIComponent(auth);
    }

    // the host is the remaining to the left of the first non-host char
    hostEnd = -1;
    for (var i = 0; i < nonHostChars.length; i++) {
      var hec = rest.indexOf(nonHostChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }
    // if we still have not hit it, then the entire thing is a host.
    if (hostEnd === -1)
      hostEnd = rest.length;

    this.host = rest.slice(0, hostEnd);
    rest = rest.slice(hostEnd);

    // pull out port.
    this.parseHost();

    // we've indicated that there is a hostname,
    // so even if it's empty, it has to be present.
    this.hostname = this.hostname || '';

    // if hostname begins with [ and ends with ]
    // assume that it's an IPv6 address.
    var ipv6Hostname = this.hostname[0] === '[' &&
        this.hostname[this.hostname.length - 1] === ']';

    // validate a little.
    if (!ipv6Hostname) {
      var hostparts = this.hostname.split(/\./);
      for (var i = 0, l = hostparts.length; i < l; i++) {
        var part = hostparts[i];
        if (!part) continue;
        if (!part.match(hostnamePartPattern)) {
          var newpart = '';
          for (var j = 0, k = part.length; j < k; j++) {
            if (part.charCodeAt(j) > 127) {
              // we replace non-ASCII char with a temporary placeholder
              // we need this to make sure size of hostname is not
              // broken by replacing non-ASCII by nothing
              newpart += 'x';
            } else {
              newpart += part[j];
            }
          }
          // we test again with ASCII char only
          if (!newpart.match(hostnamePartPattern)) {
            var validParts = hostparts.slice(0, i);
            var notHost = hostparts.slice(i + 1);
            var bit = part.match(hostnamePartStart);
            if (bit) {
              validParts.push(bit[1]);
              notHost.unshift(bit[2]);
            }
            if (notHost.length) {
              rest = '/' + notHost.join('.') + rest;
            }
            this.hostname = validParts.join('.');
            break;
          }
        }
      }
    }

    if (this.hostname.length > hostnameMaxLen) {
      this.hostname = '';
    } else {
      // hostnames are always lower case.
      this.hostname = this.hostname.toLowerCase();
    }

    if (!ipv6Hostname) {
      // IDNA Support: Returns a punycoded representation of "domain".
      // It only converts parts of the domain name that
      // have non-ASCII characters, i.e. it doesn't matter if
      // you call it with a domain that already is ASCII-only.
      this.hostname = punycode.toASCII(this.hostname);
    }

    var p = this.port ? ':' + this.port : '';
    var h = this.hostname || '';
    this.host = h + p;
    this.href += this.host;

    // strip [ and ] from the hostname
    // the host field still retains them, though
    if (ipv6Hostname) {
      this.hostname = this.hostname.substr(1, this.hostname.length - 2);
      if (rest[0] !== '/') {
        rest = '/' + rest;
      }
    }
  }

  // now rest is set to the post-host stuff.
  // chop off any delim chars.
  if (!unsafeProtocol[lowerProto]) {

    // First, make 100% sure that any "autoEscape" chars get
    // escaped, even if encodeURIComponent doesn't think they
    // need to be.
    for (var i = 0, l = autoEscape.length; i < l; i++) {
      var ae = autoEscape[i];
      if (rest.indexOf(ae) === -1)
        continue;
      var esc = encodeURIComponent(ae);
      if (esc === ae) {
        esc = escape(ae);
      }
      rest = rest.split(ae).join(esc);
    }
  }


  // chop off from the tail first.
  var hash = rest.indexOf('#');
  if (hash !== -1) {
    // got a fragment string.
    this.hash = rest.substr(hash);
    rest = rest.slice(0, hash);
  }
  var qm = rest.indexOf('?');
  if (qm !== -1) {
    this.search = rest.substr(qm);
    this.query = rest.substr(qm + 1);
    if (parseQueryString) {
      this.query = querystring.parse(this.query);
    }
    rest = rest.slice(0, qm);
  } else if (parseQueryString) {
    // no query string, but parseQueryString still requested
    this.search = '';
    this.query = {};
  }
  if (rest) this.pathname = rest;
  if (slashedProtocol[lowerProto] &&
      this.hostname && !this.pathname) {
    this.pathname = '/';
  }

  //to support http.request
  if (this.pathname || this.search) {
    var p = this.pathname || '';
    var s = this.search || '';
    this.path = p + s;
  }

  // finally, reconstruct the href based on what has been validated.
  this.href = this.format();
  return this;
};

// format a parsed object into a url string
function urlFormat(obj) {
  // ensure it's an object, and not a string url.
  // If it's an obj, this is a no-op.
  // this way, you can call url_format() on strings
  // to clean up potentially wonky urls.
  if (util.isString(obj)) obj = urlParse(obj);
  if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
  return obj.format();
}

Url.prototype.format = function() {
  var auth = this.auth || '';
  if (auth) {
    auth = encodeURIComponent(auth);
    auth = auth.replace(/%3A/i, ':');
    auth += '@';
  }

  var protocol = this.protocol || '',
      pathname = this.pathname || '',
      hash = this.hash || '',
      host = false,
      query = '';

  if (this.host) {
    host = auth + this.host;
  } else if (this.hostname) {
    host = auth + (this.hostname.indexOf(':') === -1 ?
        this.hostname :
        '[' + this.hostname + ']');
    if (this.port) {
      host += ':' + this.port;
    }
  }

  if (this.query &&
      util.isObject(this.query) &&
      Object.keys(this.query).length) {
    query = querystring.stringify(this.query);
  }

  var search = this.search || (query && ('?' + query)) || '';

  if (protocol && protocol.substr(-1) !== ':') protocol += ':';

  // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
  // unless they had them to begin with.
  if (this.slashes ||
      (!protocol || slashedProtocol[protocol]) && host !== false) {
    host = '//' + (host || '');
    if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
  } else if (!host) {
    host = '';
  }

  if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
  if (search && search.charAt(0) !== '?') search = '?' + search;

  pathname = pathname.replace(/[?#]/g, function(match) {
    return encodeURIComponent(match);
  });
  search = search.replace('#', '%23');

  return protocol + host + pathname + search + hash;
};

Url.prototype.resolve = function(relative) {
  return this.resolveObject(urlParse(relative, false, true)).format();
};

Url.prototype.resolveObject = function(relative) {
  if (util.isString(relative)) {
    var rel = new Url();
    rel.parse(relative, false, true);
    relative = rel;
  }

  var result = new Url();
  var tkeys = Object.keys(this);
  for (var tk = 0; tk < tkeys.length; tk++) {
    var tkey = tkeys[tk];
    result[tkey] = this[tkey];
  }

  // hash is always overridden, no matter what.
  // even href="" will remove it.
  result.hash = relative.hash;

  // if the relative url is empty, then there's nothing left to do here.
  if (relative.href === '') {
    result.href = result.format();
    return result;
  }

  // hrefs like //foo/bar always cut to the protocol.
  if (relative.slashes && !relative.protocol) {
    // take everything except the protocol from relative
    var rkeys = Object.keys(relative);
    for (var rk = 0; rk < rkeys.length; rk++) {
      var rkey = rkeys[rk];
      if (rkey !== 'protocol')
        result[rkey] = relative[rkey];
    }

    //urlParse appends trailing / to urls like http://www.example.com
    if (slashedProtocol[result.protocol] &&
        result.hostname && !result.pathname) {
      result.path = result.pathname = '/';
    }

    result.href = result.format();
    return result;
  }

  if (relative.protocol && relative.protocol !== result.protocol) {
    // if it's a known url protocol, then changing
    // the protocol does weird things
    // first, if it's not file:, then we MUST have a host,
    // and if there was a path
    // to begin with, then we MUST have a path.
    // if it is file:, then the host is dropped,
    // because that's known to be hostless.
    // anything else is assumed to be absolute.
    if (!slashedProtocol[relative.protocol]) {
      var keys = Object.keys(relative);
      for (var v = 0; v < keys.length; v++) {
        var k = keys[v];
        result[k] = relative[k];
      }
      result.href = result.format();
      return result;
    }

    result.protocol = relative.protocol;
    if (!relative.host && !hostlessProtocol[relative.protocol]) {
      var relPath = (relative.pathname || '').split('/');
      while (relPath.length && !(relative.host = relPath.shift()));
      if (!relative.host) relative.host = '';
      if (!relative.hostname) relative.hostname = '';
      if (relPath[0] !== '') relPath.unshift('');
      if (relPath.length < 2) relPath.unshift('');
      result.pathname = relPath.join('/');
    } else {
      result.pathname = relative.pathname;
    }
    result.search = relative.search;
    result.query = relative.query;
    result.host = relative.host || '';
    result.auth = relative.auth;
    result.hostname = relative.hostname || relative.host;
    result.port = relative.port;
    // to support http.request
    if (result.pathname || result.search) {
      var p = result.pathname || '';
      var s = result.search || '';
      result.path = p + s;
    }
    result.slashes = result.slashes || relative.slashes;
    result.href = result.format();
    return result;
  }

  var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
      isRelAbs = (
          relative.host ||
          relative.pathname && relative.pathname.charAt(0) === '/'
      ),
      mustEndAbs = (isRelAbs || isSourceAbs ||
                    (result.host && relative.pathname)),
      removeAllDots = mustEndAbs,
      srcPath = result.pathname && result.pathname.split('/') || [],
      relPath = relative.pathname && relative.pathname.split('/') || [],
      psychotic = result.protocol && !slashedProtocol[result.protocol];

  // if the url is a non-slashed url, then relative
  // links like ../.. should be able
  // to crawl up to the hostname, as well.  This is strange.
  // result.protocol has already been set by now.
  // Later on, put the first path part into the host field.
  if (psychotic) {
    result.hostname = '';
    result.port = null;
    if (result.host) {
      if (srcPath[0] === '') srcPath[0] = result.host;
      else srcPath.unshift(result.host);
    }
    result.host = '';
    if (relative.protocol) {
      relative.hostname = null;
      relative.port = null;
      if (relative.host) {
        if (relPath[0] === '') relPath[0] = relative.host;
        else relPath.unshift(relative.host);
      }
      relative.host = null;
    }
    mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
  }

  if (isRelAbs) {
    // it's absolute.
    result.host = (relative.host || relative.host === '') ?
                  relative.host : result.host;
    result.hostname = (relative.hostname || relative.hostname === '') ?
                      relative.hostname : result.hostname;
    result.search = relative.search;
    result.query = relative.query;
    srcPath = relPath;
    // fall through to the dot-handling below.
  } else if (relPath.length) {
    // it's relative
    // throw away the existing file, and take the new path instead.
    if (!srcPath) srcPath = [];
    srcPath.pop();
    srcPath = srcPath.concat(relPath);
    result.search = relative.search;
    result.query = relative.query;
  } else if (!util.isNullOrUndefined(relative.search)) {
    // just pull out the search.
    // like href='?foo'.
    // Put this after the other two cases because it simplifies the booleans
    if (psychotic) {
      result.hostname = result.host = srcPath.shift();
      //occationaly the auth can get stuck only in host
      //this especially happens in cases like
      //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
      var authInHost = result.host && result.host.indexOf('@') > 0 ?
                       result.host.split('@') : false;
      if (authInHost) {
        result.auth = authInHost.shift();
        result.host = result.hostname = authInHost.shift();
      }
    }
    result.search = relative.search;
    result.query = relative.query;
    //to support http.request
    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
      result.path = (result.pathname ? result.pathname : '') +
                    (result.search ? result.search : '');
    }
    result.href = result.format();
    return result;
  }

  if (!srcPath.length) {
    // no path at all.  easy.
    // we've already handled the other stuff above.
    result.pathname = null;
    //to support http.request
    if (result.search) {
      result.path = '/' + result.search;
    } else {
      result.path = null;
    }
    result.href = result.format();
    return result;
  }

  // if a url ENDs in . or .., then it must get a trailing slash.
  // however, if it ends in anything else non-slashy,
  // then it must NOT get a trailing slash.
  var last = srcPath.slice(-1)[0];
  var hasTrailingSlash = (
      (result.host || relative.host || srcPath.length > 1) &&
      (last === '.' || last === '..') || last === '');

  // strip single dots, resolve double dots to parent dir
  // if the path tries to go above the root, `up` ends up > 0
  var up = 0;
  for (var i = srcPath.length; i >= 0; i--) {
    last = srcPath[i];
    if (last === '.') {
      srcPath.splice(i, 1);
    } else if (last === '..') {
      srcPath.splice(i, 1);
      up++;
    } else if (up) {
      srcPath.splice(i, 1);
      up--;
    }
  }

  // if the path is allowed to go above the root, restore leading ..s
  if (!mustEndAbs && !removeAllDots) {
    for (; up--; up) {
      srcPath.unshift('..');
    }
  }

  if (mustEndAbs && srcPath[0] !== '' &&
      (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
    srcPath.unshift('');
  }

  if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
    srcPath.push('');
  }

  var isAbsolute = srcPath[0] === '' ||
      (srcPath[0] && srcPath[0].charAt(0) === '/');

  // put the host back
  if (psychotic) {
    result.hostname = result.host = isAbsolute ? '' :
                                    srcPath.length ? srcPath.shift() : '';
    //occationaly the auth can get stuck only in host
    //this especially happens in cases like
    //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
    var authInHost = result.host && result.host.indexOf('@') > 0 ?
                     result.host.split('@') : false;
    if (authInHost) {
      result.auth = authInHost.shift();
      result.host = result.hostname = authInHost.shift();
    }
  }

  mustEndAbs = mustEndAbs || (result.host && srcPath.length);

  if (mustEndAbs && !isAbsolute) {
    srcPath.unshift('');
  }

  if (!srcPath.length) {
    result.pathname = null;
    result.path = null;
  } else {
    result.pathname = srcPath.join('/');
  }

  //to support request.http
  if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
    result.path = (result.pathname ? result.pathname : '') +
                  (result.search ? result.search : '');
  }
  result.auth = relative.auth || result.auth;
  result.slashes = result.slashes || relative.slashes;
  result.href = result.format();
  return result;
};

Url.prototype.parseHost = function() {
  var host = this.host;
  var port = portPattern.exec(host);
  if (port) {
    port = port[0];
    if (port !== ':') {
      this.port = port.substr(1);
    }
    host = host.substr(0, host.length - port.length);
  }
  if (host) this.hostname = host;
};

// put toString() in a class so it isn't included in the props when checked for equality
var MockURLImplementation = /** @class */ (function () {
    function MockURLImplementation() {
    }
    MockURLImplementation.prototype.toString = function () {
        return formatURL(this);
    };
    return MockURLImplementation;
}());
function parseURL(url$$1) {
    var urlObject = new MockURLImplementation();
    if (!url$$1) {
        return urlObject;
    }
    var parsedURL = parse(url$$1, true);
    if (parsedURL.protocol) {
        urlObject.protocol = parsedURL.protocol.substr(0, parsedURL.protocol.length - 1);
    }
    if (parsedURL.auth) {
        var _a = parsedURL.auth.split(':'), username = _a[0], password = _a[1];
        if (username && password) {
            urlObject.username = username;
            urlObject.password = password;
        }
        else {
            urlObject.username = username;
        }
    }
    if (parsedURL.hostname) {
        urlObject.host = parsedURL.hostname;
    }
    if (parsedURL.port) {
        urlObject.port = parseInt(parsedURL.port, 10);
    }
    if (parsedURL.pathname) {
        urlObject.path = parsedURL.pathname;
    }
    if (parsedURL.query) {
        urlObject.query = parsedURL.query;
    }
    if (parsedURL.hash) {
        urlObject.hash = parsedURL.hash;
    }
    return urlObject;
}
function formatURL(url$$1) {
    var obj = {
        protocol: url$$1.protocol,
        auth: url$$1.username && url$$1.password
            ? url$$1.username + ":" + url$$1.password
            : url$$1.username,
        hostname: url$$1.host,
        port: typeof url$$1.port === 'number' ? String(url$$1.port) : url$$1.port,
        pathname: url$$1.path,
        query: url$$1.query,
        hash: url$$1.hash
    };
    return format(obj);
}

var FORBIDDEN_METHODS$1 = ['CONNECT', 'TRACE', 'TRACK'];
var UPPERCASE_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
var MockRequest = /** @class */ (function () {
    function MockRequest() {
        this._method = 'GET';
        this._url = parseURL('');
        this._headers = {};
        this._body = null;
    }
    MockRequest.prototype.method = function (method) {
        if (typeof method !== 'undefined') {
            if (FORBIDDEN_METHODS$1.indexOf(method.toUpperCase()) !== -1) {
                throw new Error("xhr-mock: Method \"" + method + "\" is forbidden.");
            }
            if (UPPERCASE_METHODS.indexOf(method.toUpperCase()) !== -1) {
                this._method = method.toUpperCase();
            }
            else {
                this._method = method;
            }
            return this;
        }
        else {
            return this._method;
        }
    };
    MockRequest.prototype.url = function (url) {
        if (typeof url === 'string') {
            this._url = parseURL(url);
            return this;
        }
        else {
            return this._url;
        }
    };
    MockRequest.prototype.header = function (name, value) {
        if (typeof value !== 'undefined') {
            this._headers[name.toLowerCase()] = value;
            return this;
        }
        else {
            return this._headers[name.toLowerCase()] || null;
        }
    };
    MockRequest.prototype.headers = function (headers) {
        if (typeof headers === 'object') {
            for (var name in headers) {
                if (headers.hasOwnProperty(name)) {
                    this.header(name, headers[name]);
                }
            }
            return this;
        }
        else {
            return this._headers;
        }
    };
    MockRequest.prototype.body = function (body) {
        if (typeof body !== 'undefined') {
            this._body = body;
            return this;
        }
        else {
            return this._body;
        }
    };
    return MockRequest;
}());

var MockEvent = /** @class */ (function () {
    function MockEvent(type, eventInitDict) {
        this.eventPhase = 0;
        this.type = type || '';
        if (eventInitDict) {
            var _a = eventInitDict.scoped, scoped = _a === void 0 ? false : _a, _b = eventInitDict.bubbles, bubbles = _b === void 0 ? false : _b, _c = eventInitDict.cancelable, cancelable = _c === void 0 ? false : _c;
            this.scoped = scoped;
            this.bubbles = bubbles;
            this.cancelable = cancelable;
        }
    }
    MockEvent.prototype.initEvent = function (eventTypeArg, canBubbleArg, cancelableArg) {
        throw new Error();
    };
    MockEvent.prototype.preventDefault = function () {
        throw new Error();
    };
    MockEvent.prototype.stopImmediatePropagation = function () {
        throw new Error();
    };
    MockEvent.prototype.stopPropagation = function () {
        throw new Error();
    };
    MockEvent.prototype.deepPath = function () {
        throw new Error();
    };
    return MockEvent;
}());

var MockProgressEvent = /** @class */ (function (_super) {
    __extends(MockProgressEvent, _super);
    function MockProgressEvent(type, eventInitDict) {
        var _this = _super.call(this, type, eventInitDict) || this;
        if (eventInitDict) {
            var _a = eventInitDict.lengthComputable, lengthComputable = _a === void 0 ? false : _a, _b = eventInitDict.loaded, loaded = _b === void 0 ? 0 : _b, _c = eventInitDict.total, total = _c === void 0 ? 0 : _c;
            _this.lengthComputable = lengthComputable;
            _this.loaded = loaded;
            _this.total = total;
        }
        return _this;
    }
    MockProgressEvent.prototype.initProgressEvent = function (typeArg, canBubbleArg, cancelableArg, lengthComputableArg, loadedArg, totalArg) {
        throw new Error();
    };
    return MockProgressEvent;
}(MockEvent));

var MockEventTarget = /** @class */ (function () {
    function MockEventTarget() {
        this.listeners = {};
    }
    MockEventTarget.prototype.addEventListener = function (type, listener, options) {
        this.listeners = this.listeners || {};
        if (!listener) {
            return;
        }
        if (!this.listeners[type]) {
            this.listeners[type] = [];
        }
        //handleEvent
        if (this.listeners[type].indexOf(listener) === -1) {
            this.listeners[type].push(listener);
        }
    };
    MockEventTarget.prototype.removeEventListener = function (type, listener, options) {
        this.listeners = this.listeners || {};
        if (!listener) {
            return;
        }
        if (!this.listeners[type]) {
            return;
        }
        var index = this.listeners[type].indexOf(listener);
        if (index !== -1) {
            this.listeners[type].splice(index, 1);
        }
    };
    MockEventTarget.prototype.dispatchEvent = function (event) {
        var _this = this;
        this.listeners = this.listeners || {};
        //set the event target
        event.target = this;
        event.currentTarget = this;
        //call any built-in listeners
        //FIXME: the listener should be added on set
        var method = this["on" + event.type];
        if (method) {
            method.call(this, event);
        }
        if (!this.listeners[event.type]) {
            return true;
        }
        this.listeners[event.type].forEach(function (listener) {
            if (typeof listener === 'function') {
                listener.call(_this, event);
            }
            else {
                listener.handleEvent.call(_this, event);
            }
        });
        return true; //TODO: return type based on .cancellable and .preventDefault()
    };
    return MockEventTarget;
}());

// @ts-ignore: https://github.com/jameslnewell/xhr-mock/issues/45
var MockXMLHttpRequestEventTarget = /** @class */ (function (_super) {
    __extends(MockXMLHttpRequestEventTarget, _super);
    function MockXMLHttpRequestEventTarget() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return MockXMLHttpRequestEventTarget;
}(MockEventTarget));

// @ts-ignore: https://github.com/jameslnewell/xhr-mock/issues/45
var MockXMLHttpRequestUpload = /** @class */ (function (_super) {
    __extends(MockXMLHttpRequestUpload, _super);
    function MockXMLHttpRequestUpload() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return MockXMLHttpRequestUpload;
}(MockXMLHttpRequestEventTarget));

var MockError = /** @class */ (function (_super) {
    __extends(MockError, _super);
    function MockError(message) {
        var _this = _super.call(this, message) || this;
        // hack to make instanceof work @see https://stackoverflow.com/questions/31626231/custom-error-class-in-typescript
        Object.setPrototypeOf(_this, MockError.prototype);
        return _this;
    }
    return MockError;
}(Error));

function isPromiseLike(arg) {
    return arg && arg.then !== undefined;
}

var NO_RESPONSE_ERROR = new MockError('No handler returned a response for the request.');
function sync(handlers, request, response) {
    for (var i = 0; i < handlers.length; ++i) {
        var result = handlers[i](request, response);
        if (result) {
            if (isPromiseLike(result)) {
                throw new MockError('A handler returned a Promise<MockResponse> for a synchronous request.');
            }
            return result;
        }
    }
    throw NO_RESPONSE_ERROR;
}
function async(handlers, request, response) {
    return handlers
        .reduce(function (promise, handler) {
        return promise.then(function (result) {
            if (!result) {
                return handler(request, response);
            }
            return result;
        });
    }, Promise.resolve(undefined))
        .then(function (result) {
        if (!result) {
            throw NO_RESPONSE_ERROR;
        }
        return result;
    });
}

function convertRequestToString(req) {
    var headers = Object.keys(req.headers()).map(function (name) { return name + ": " + req.header(name); });
    var body = req.body() ? req.body() : '';
    return req.method() + " " + req.url() + " HTTP/1.1\n" + (headers ? headers.join('\n') + "\n" : '') + "\n" + (body ? body : '') + "\n";
}
function indentSuccessiveLines(string, indent) {
    return string
        .split('\n')
        .map(function (line, index) { return Array(indent + 1).join(' ') + line; })
        .join('\n');
}
function formatError(msg, req, err) {
    return "xhr-mock: " + msg + "\n\n  " + indentSuccessiveLines(convertRequestToString(req), 2).trim() + "\n  " + (err !== undefined
        ? "\n" + indentSuccessiveLines((err && err.stack) || (err && err.message) || "Error: " + err, 2)
        : '') + "\n";
}

var notImplementedError = new MockError("This feature hasn't been implmented yet. Please submit an Issue or Pull Request on Github.");
// implemented according to https://xhr.spec.whatwg.org/
var FORBIDDEN_METHODS = ['CONNECT', 'TRACE', 'TRACK'];
var ReadyState;
(function (ReadyState) {
    ReadyState[ReadyState["UNSENT"] = 0] = "UNSENT";
    ReadyState[ReadyState["OPENED"] = 1] = "OPENED";
    ReadyState[ReadyState["HEADERS_RECEIVED"] = 2] = "HEADERS_RECEIVED";
    ReadyState[ReadyState["LOADING"] = 3] = "LOADING";
    ReadyState[ReadyState["DONE"] = 4] = "DONE";
})(ReadyState || (ReadyState = {}));
function calculateProgress(req) {
    var header = req.header('content-length');
    var body = req.body();
    var lengthComputable = false;
    var total = 0;
    if (header) {
        var contentLength = parseInt(header, 10);
        if (contentLength !== NaN) {
            lengthComputable = true;
            total = contentLength;
        }
    }
    return {
        lengthComputable: lengthComputable,
        loaded: (body && body.length) || 0,
        total: total
    };
}
// @ts-ignore: https://github.com/jameslnewell/xhr-mock/issues/45
var MockXMLHttpRequest = /** @class */ (function (_super) {
    __extends(MockXMLHttpRequest, _super);
    function MockXMLHttpRequest() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.UNSENT = ReadyState.UNSENT;
        _this.OPENED = ReadyState.OPENED;
        _this.HEADERS_RECEIVED = ReadyState.HEADERS_RECEIVED;
        _this.LOADING = ReadyState.LOADING;
        _this.DONE = ReadyState.DONE;
        //some libraries (like Mixpanel) use the presence of this field to check if XHR is properly supported
        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
        _this.withCredentials = false;
        _this.req = new MockRequest();
        _this.res = new MockResponse();
        _this.responseType = '';
        _this.responseURL = '';
        _this._timeout = 0;
        // @ts-ignore: https://github.com/jameslnewell/xhr-mock/issues/45
        _this.upload = new MockXMLHttpRequestUpload();
        _this.readyState = MockXMLHttpRequest.UNSENT;
        // flags
        _this.isSynchronous = false;
        _this.isSending = false;
        _this.isUploadComplete = false;
        _this.isAborted = false;
        _this.isTimedOut = false;
        return _this;
    }
    /**
     * Add a mock handler
     */
    MockXMLHttpRequest.addHandler = function (fn) {
        this.handlers.push(fn);
    };
    /**
     * Remove a mock handler
     */
    MockXMLHttpRequest.removeHandler = function (fn) {
        throw notImplementedError;
    };
    /**
     * Remove all request handlers
     */
    MockXMLHttpRequest.removeAllHandlers = function () {
        this.handlers = [];
    };
    Object.defineProperty(MockXMLHttpRequest.prototype, "timeout", {
        get: function () {
            return this._timeout;
        },
        set: function (timeout) {
            if (timeout !== 0 && this.isSynchronous) {
                throw new MockError('Timeouts cannot be set for synchronous requests made from a document.');
            }
            this._timeout = timeout;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(MockXMLHttpRequest.prototype, "response", {
        // https://xhr.spec.whatwg.org/#the-response-attribute
        get: function () {
            if (this.responseType === '' || this.responseType === 'text') {
                if (this.readyState !== this.LOADING && this.readyState !== this.DONE) {
                    return '';
                }
                return this.responseText;
            }
            if (this.readyState !== this.DONE) {
                return null;
            }
            var body = this.res.body();
            if (!body) {
                return null;
            }
            if (this.responseType === 'json' && typeof body === 'string') {
                try {
                    return JSON.parse(this.responseText);
                }
                catch (error) {
                    return null;
                }
            }
            if (this.responseType === 'blob' && typeof body === 'string') {
                try {
                    throw notImplementedError;
                }
                catch (error) {
                    return null;
                }
            }
            if (this.responseType === 'arraybuffer' && typeof body === 'string') {
                try {
                    throw notImplementedError;
                }
                catch (error) {
                    return null;
                }
            }
            if (this.responseType === 'document' && typeof body === 'string') {
                try {
                    throw notImplementedError;
                }
                catch (error) {
                    return null;
                }
            }
            // rely on the mock to do the right thing with an arraybuffer, blob or document
            return body;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(MockXMLHttpRequest.prototype, "responseText", {
        get: function () {
            return this.res.body() || '';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(MockXMLHttpRequest.prototype, "responseXML", {
        get: function () {
            throw notImplementedError;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(MockXMLHttpRequest.prototype, "status", {
        get: function () {
            return this.res.status();
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(MockXMLHttpRequest.prototype, "statusText", {
        get: function () {
            return this.res.reason();
        },
        enumerable: true,
        configurable: true
    });
    MockXMLHttpRequest.prototype.getAllResponseHeaders = function () {
        // I'm pretty sure this fn can return null, but TS types say no
        // if (this.readyState < MockXMLHttpRequest.HEADERS_RECEIVED) {
        //   return null;
        // }
        var headers = this.res.headers();
        var result = Object.keys(headers)
            .map(function (name) { return name + ": " + headers[name] + "\r\n"; })
            .join('');
        return result;
    };
    MockXMLHttpRequest.prototype.getResponseHeader = function (name) {
        if (this.readyState < MockXMLHttpRequest.HEADERS_RECEIVED) {
            return null;
        }
        return this.res.header(name);
    };
    MockXMLHttpRequest.prototype.setRequestHeader = function (name, value) {
        if (this.readyState < MockXMLHttpRequest.OPENED) {
            throw new MockError('xhr must be OPENED.');
        }
        this.req.header(name, value);
    };
    MockXMLHttpRequest.prototype.overrideMimeType = function (mime) {
        throw notImplementedError;
    };
    MockXMLHttpRequest.prototype.open = function (method, url, async$$1, username, password) {
        if (async$$1 === void 0) { async$$1 = true; }
        if (username === void 0) { username = null; }
        if (password === void 0) { password = null; }
        // if method is not a method, then throw a "SyntaxError" DOMException
        // if method is a forbidden method, then throw a "SecurityError" DOMException
        if (FORBIDDEN_METHODS.indexOf(method) !== -1) {
            throw new MockError("Method " + method + " is forbidden.");
        }
        // normalize method
        method = method.toUpperCase();
        // let parsedURL be the result of parsing url with settingsObject’s API base URL and settingsObject’s API URL character encoding
        // if parsedURL is failure, then throw a "SyntaxError" DOMException
        var fullURL = parseURL(url);
        // if the async argument is omitted, set async to true, and set username and password to null.
        // if parsedURL’s host is non-null, run these substeps:
        // if the username argument is not null, set the username given parsedURL and username
        // if the password argument is not null, set the password given parsedURL and password
        fullURL.username = username || '';
        fullURL.password = (username && password) || '';
        // if async is false, current global object is a Window object, and the timeout attribute value
        // is not zero or the responseType attribute value is not the empty string, then throw an "InvalidAccessError" DOMException.
        if (!async$$1 && (this._timeout !== 0 || this.responseType !== '')) {
            throw new MockError('InvalidAccessError');
        }
        // terminate the ongoing fetch operated by the XMLHttpRequest object
        if (this.isSending) {
            throw new MockError('Unable to terminate the previous request');
        }
        // set variables associated with the object as follows:
        // - unset the send() flag and upload listener flag
        // - set the synchronous flag, if async is false, and unset the synchronous flag otherwise
        // - set request method to method
        // - set request URL to parsedURL
        // - empty author request headers
        this.isSending = false;
        this.isSynchronous = !async$$1;
        this.req
            .method(method)
            .headers({})
            .url(formatURL(fullURL));
        this.applyNetworkError();
        // if the state is not opened, run these substeps:
        if (this.readyState !== this.OPENED) {
            // set state to opened
            this.readyState = MockXMLHttpRequest.OPENED;
            // fire an event named readystatechange
            this.dispatchEvent(new MockEvent('readystatechange'));
        }
    };
    MockXMLHttpRequest.prototype.sendSync = function () {
        // let response be the result of fetching req
        var res;
        try {
            res = sync(MockXMLHttpRequest.handlers, this.req, this.res);
            // if the timeout attribute value is not zero, then set the timed out flag and terminate fetching if it has not returned within the amount of milliseconds from the timeout.
            // TODO: check if timeout was elapsed
            //if response’s body is null, then run handle response end-of-body and return
            // let reader be the result of getting a reader from response’s body’s stream
            // let promise be the result of reading all bytes from response’s body’s stream with reader
            // wait for promise to be fulfilled or rejected
            // if promise is fulfilled with bytes, then append bytes to received bytes
            // run handle response end-of-body for response
            this.handleResponseBody(res);
        }
        catch (error) {
            MockXMLHttpRequest.errorCallback({ req: this.req, err: error });
            this.handleError(error);
        }
    };
    MockXMLHttpRequest.prototype.sendAsync = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            var req, progress, progress_1, res, error_1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        req = this.req;
                        progress = calculateProgress(this.res);
                        this.dispatchEvent(new MockProgressEvent('loadstart', __assign({}, progress, { loaded: 0 })));
                        // if the upload complete flag is unset and upload listener flag is set, then fire a progress
                        // event named loadstart on the XMLHttpRequestUpload object with 0 and req’s body’s total bytes.
                        if (!this.isUploadComplete) {
                            progress_1 = calculateProgress(this.req);
                            this.upload.dispatchEvent(new MockProgressEvent('loadstart', __assign({}, progress_1, { loaded: 0 })));
                        }
                        // if state is not opened or the send() flag is unset, then return.
                        if (this.readyState !== this.OPENED || !this.isSending) {
                            return [2 /*return*/];
                        }
                        // fetch req. Handle the tasks queued on the networking task source per below
                        // run these subsubsteps in parallel:
                        // wait until either req’s done flag is set or
                        // the timeout attribute value number of milliseconds has passed since these subsubsteps started
                        // while timeout attribute value is not zero
                        // if req’s done flag is unset, then set the timed out flag and terminate fetching
                        if (this._timeout !== 0) {
                            // @ts-ignore: wants a NodeJS.Timer because of @types/node
                            this._timeoutTimer = setTimeout(function () {
                                _this.isTimedOut = true;
                                _this.handleError();
                            }, this._timeout);
                        }
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, async(MockXMLHttpRequest.handlers, this.req, this.res)];
                    case 2:
                        res = _a.sent();
                        //we've received a response before the timeout so we don't want to timeout
                        clearTimeout(this._timeoutTimer);
                        if (this.isAborted || this.isTimedOut) {
                            return [2 /*return*/]; // these cases will already have been handled
                        }
                        this.sendRequest(req);
                        this.receiveResponse(res);
                        return [3 /*break*/, 4];
                    case 3:
                        error_1 = _a.sent();
                        //we've received an error before the timeout so we don't want to timeout
                        clearTimeout(this._timeoutTimer);
                        if (this.isAborted || this.isTimedOut) {
                            return [2 /*return*/]; // these cases will already have been handled
                        }
                        MockXMLHttpRequest.errorCallback({ req: this.req, err: error_1 });
                        this.handleError(error_1);
                        return [3 /*break*/, 4];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    MockXMLHttpRequest.prototype.applyNetworkError = function () {
        // a network error is a response whose status is always 0, status message is always the
        // empty byte sequence, header list is always empty, body is always null, and
        // trailer is always empty
        this.res
            .status(0)
            .reason('')
            .headers({})
            .body(null);
    };
    // @see https://xhr.spec.whatwg.org/#request-error-steps
    MockXMLHttpRequest.prototype.reportError = function (event) {
        // set state to done
        this.readyState = this.DONE;
        // unset the send() flag
        this.isSending = false;
        // set response to network error
        this.applyNetworkError();
        // if the synchronous flag is set, throw an exception exception
        if (this.isSynchronous) {
            throw new MockError('An error occurred whilst sending a synchronous request.');
        }
        // fire an event named readystatechange
        this.dispatchEvent(new MockEvent('readystatechange'));
        // if the upload complete flag is unset, follow these substeps:
        if (!this.isUploadComplete) {
            // set the upload complete flag
            this.isUploadComplete = true;
            // if upload listener flag is unset, then terminate these substeps
            // NOTE: not sure why this is necessary - if there's no listeners  listening, then the
            // following events have no impact
            var uploadProgress = calculateProgress(this.req);
            // fire a progress event named event on the XMLHttpRequestUpload object with 0 and 0
            this.upload.dispatchEvent(new MockProgressEvent(event, uploadProgress));
            // fire a progress event named loadend on the XMLHttpRequestUpload object with 0 and 0
            this.upload.dispatchEvent(new MockProgressEvent('loadend', uploadProgress));
        }
        var downloadProgress = calculateProgress(this.res);
        // fire a progress event named event with 0 and 0
        this.dispatchEvent(new MockProgressEvent(event, downloadProgress));
        // fire a progress event named loadend with 0 and 0
        this.dispatchEvent(new MockProgressEvent('loadend', downloadProgress));
    };
    MockXMLHttpRequest.prototype.sendRequest = function (req) {
        if (this.isUploadComplete) {
            return;
        }
        // if not roughly 50ms have passed since these subsubsteps were last invoked, terminate these subsubsteps
        // TODO:
        // If upload listener flag is set, then fire a progress event named progress on the
        // XMLHttpRequestUpload object with request’s body’s transmitted bytes and request’s body’s
        // total bytes
        // const progress = getProgress(this.req);
        // this.upload.dispatchEvent(new MockProgressEvent('progress', {
        //   ...progress,
        //   loaded: %
        // }))
        // TODO: repeat this in a timeout to simulate progress events
        // TODO: dispatch total, length and lengthComputable values
        // set the upload complete flag
        this.isUploadComplete = true;
        // if upload listener flag is unset, then terminate these subsubsteps.
        // NOTE: it doesn't really matter if we emit these events and noone is listening
        // let transmitted be request’s body’s transmitted bytes
        // let length be request’s body’s total bytes
        var progress = calculateProgress(this.req);
        // fire a progress event named progress on the XMLHttpRequestUpload object with transmitted and length
        this.upload.dispatchEvent(new MockProgressEvent('progress', progress));
        // fire a progress event named load on the XMLHttpRequestUpload object with transmitted and length
        this.upload.dispatchEvent(new MockProgressEvent('load', progress));
        // fire a progress event named loadend on the XMLHttpRequestUpload object with transmitted and length
        this.upload.dispatchEvent(new MockProgressEvent('loadend', progress));
    };
    MockXMLHttpRequest.prototype.receiveResponse = function (res) {
        // set state to headers received
        this.readyState = this.HEADERS_RECEIVED;
        // fire an event named readystatechange
        this.dispatchEvent(new MockEvent('readystatechange'));
        // if state is not headers received, then return
        // NOTE: is that really necessary, we've just change the state a second ago
        // if response’s body is null, then run handle response end-of-body and return
        if (res.body() === null) {
            this.handleResponseBody(res);
            return;
        }
        // let reader be the result of getting a reader from response’s body’s stream
        // let read be the result of reading a chunk from response’s body’s stream with reader
        // When read is fulfilled with an object whose done property is false and whose value property
        // is a Uint8Array object, run these subsubsubsteps and then run the above subsubstep again:
        // TODO:
        // append the value property to received bytes
        // if not roughly 50ms have passed since these subsubsubsteps were last invoked, then terminate
        // these subsubsubsteps
        // TODO:
        // if state is headers received, then set state to loading
        // NOTE: why wouldn't it be headers received?
        this.readyState = this.LOADING;
        // fire an event named readystatechange
        this.dispatchEvent(new MockEvent('readystatechange'));
        // fire a progress event named progress with response’s body’s transmitted bytes and response’s
        // body’s total bytes
        // TODO: repeat to simulate progress
        // const progress = calculateProgress(res);
        // this.dispatchEvent(new MockProgressEvent('progress', {
        //   ...progress,
        //   loaded: %
        // }));
        // when read is fulfilled with an object whose done property is true, run handle response
        // end-of-body for response
        // when read is rejected with an exception, run handle errors for response
        // NOTE: we don't handle this error case
        this.handleResponseBody(res);
    };
    // @see https://xhr.spec.whatwg.org/#handle-errors
    MockXMLHttpRequest.prototype.handleError = function (error) {
        // if the send() flag is unset, return
        if (!this.isSending) {
            return;
        }
        // if the timed out flag is set, then run the request error steps for event timeout and exception TimeoutError
        if (this.isTimedOut) {
            this.reportError('timeout');
            return;
        }
        // otherwise, if response’s body’s stream is errored, then:
        // NOTE: we're not handling this event
        // if () {
        //   // set state to done
        //   this.readyState = this.DONE;
        //   // unset the send() flag
        //   this.isSending = false;
        //   // set response to a network error
        //   this.applyNetworkError();
        //   return;
        // }
        // otherwise, if response’s aborted flag is set, then run the request error steps for event abort and exception AbortError
        if (this.isAborted) {
            this.reportError('abort');
            return;
        }
        // if response is a network error, run the request error steps for event error and exception NetworkError
        // NOTE: we assume all other calls are network errors
        this.reportError('error');
    };
    // @see https://xhr.spec.whatwg.org/#handle-response-end-of-body
    MockXMLHttpRequest.prototype.handleResponseBody = function (res) {
        this.res = res;
        // let transmitted be response’s body’s transmitted bytes
        // let length be response’s body’s total bytes.
        var progress = calculateProgress(res);
        // if the synchronous flag is unset, update response’s body using response
        if (!this.isSynchronous) {
            // fire a progress event named progress with transmitted and length
            this.dispatchEvent(new MockProgressEvent('progress', progress));
        }
        // set state to done
        this.readyState = this.DONE;
        // unset the send() flag
        this.isSending = false;
        // fire an event named readystatechange
        this.dispatchEvent(new MockEvent('readystatechange'));
        // fire a progress event named load with transmitted and length
        this.dispatchEvent(new MockProgressEvent('load', progress));
        // fire a progress event named loadend with transmitted and length
        this.dispatchEvent(new MockProgressEvent('loadend', progress));
    };
    MockXMLHttpRequest.prototype.send = function (body) {
        // if state is not opened, throw an InvalidStateError exception
        if (this.readyState !== MockXMLHttpRequest.OPENED) {
            throw new MockError('Please call MockXMLHttpRequest.open() before MockXMLHttpRequest.send().');
        }
        // if the send() flag is set, throw an InvalidStateError exception
        if (this.isSending) {
            throw new MockError('MockXMLHttpRequest.send() has already been called.');
        }
        // if the request method is GET or HEAD, set body to null
        if (this.req.method() === 'GET' || this.req.method() === 'HEAD') {
            body = null;
        }
        // if body is null, go to the next step otherwise, let encoding and mimeType be null, and then follow these rules, switching on body
        var encoding;
        var mimeType;
        if (body !== null && body !== undefined) {
            if (typeof Document !== 'undefined' &&
                typeof XMLDocument !== 'undefined' &&
                body instanceof Document) {
                // Set encoding to `UTF-8`.
                // Set mimeType to `text/html` if body is an HTML document, and to `application/xml` otherwise. Then append `;charset=UTF-8` to mimeType.
                // Set request body to body, serialized, converted to Unicode, and utf-8 encoded.
                encoding = 'UTF-8';
                mimeType =
                    body instanceof XMLDocument ? 'application/xml' : 'text/html';
            }
            else {
                // If body is a string, set encoding to `UTF-8`.
                // Set request body and mimeType to the result of extracting body.
                // https://fetch.spec.whatwg.org/#concept-bodyinit-extract
                if (typeof Blob !== 'undefined' && body instanceof Blob) {
                    mimeType = body.type;
                }
                else if (typeof FormData !== 'undefined' &&
                    body instanceof FormData) {
                    mimeType = 'multipart/form-data; boundary=----XHRMockFormBoundary';
                }
                else if (typeof URLSearchParams !== 'undefined' &&
                    body instanceof URLSearchParams) {
                    encoding = 'UTF-8';
                    mimeType = 'application/x-www-form-urlencoded';
                }
                else if (typeof body === 'string') {
                    encoding = 'UTF-8';
                    mimeType = 'text/plain';
                }
                else {
                    throw notImplementedError;
                }
            }
            // if mimeType is non-null and author request headers does not contain `Content-Type`, then append `Content-Type`/mimeType to author request headers.
            // otherwise, if the header whose name is a byte-case-insensitive match for `Content-Type` in author request headers has a value that is a valid MIME type,
            //    which has a `charset` parameter whose value is not a byte-case-insensitive match for encoding, and encoding is not null, then set all the `charset` parameters
            //    whose value is not a byte-case-insensitive match for encoding of that header’s value to encoding.
            // chrome seems to forget the second case ^^^
            var contentType = this.req.header('content-type');
            if (!contentType) {
                this.req.header('content-type', encoding ? mimeType + "; charset=" + encoding : mimeType);
            }
            this.req.body(body);
        }
        // if one or more event listeners are registered on the associated XMLHttpRequestUpload object, then set upload listener flag
        // Note: not really necessary since dispatching an event to no listeners doesn't hurt anybody
        //TODO: check CORs
        // unset the upload complete flag
        this.isUploadComplete = false;
        // unset the timed out flag
        this.isTimedOut = false;
        // if req’s body is null, set the upload complete flag
        if (body === null || body === undefined) {
            this.isUploadComplete = true;
        }
        // set the send() flag
        this.isSending = true;
        if (this.isSynchronous) {
            this.sendSync();
        }
        else {
            this.sendAsync();
        }
    };
    MockXMLHttpRequest.prototype.abort = function () {
        //we've cancelling the response before the timeout period so we don't want to timeout
        clearTimeout(this._timeoutTimer);
        // terminate the ongoing fetch with the aborted flag set
        this.isAborted = true;
        // if state is either opened with the send() flag set, headers received, or loading,
        // run the request error steps for event
        if (this.readyState === this.OPENED ||
            this.readyState === this.HEADERS_RECEIVED ||
            this.readyState === this.LOADING) {
            this.reportError('abort');
        }
        // if state is done, then set state to unsent and response to a network error
        if (this.readyState === this.DONE) {
            this.readyState = this.UNSENT;
            this.applyNetworkError();
            return;
        }
    };
    MockXMLHttpRequest.prototype.msCachingEnabled = function () {
        return false;
    };
    MockXMLHttpRequest.UNSENT = ReadyState.UNSENT;
    MockXMLHttpRequest.OPENED = ReadyState.OPENED;
    MockXMLHttpRequest.HEADERS_RECEIVED = ReadyState.HEADERS_RECEIVED;
    MockXMLHttpRequest.LOADING = ReadyState.LOADING;
    MockXMLHttpRequest.DONE = ReadyState.DONE;
    MockXMLHttpRequest.handlers = [];
    MockXMLHttpRequest.errorCallback = function (_a) {
        var req = _a.req, err = _a.err;
        if (err instanceof MockError) {
            console.error(formatError(err.message, req));
        }
        else {
            console.error(formatError('A handler returned an error for the request.', req, err));
        }
    };
    return MockXMLHttpRequest;
}(MockXMLHttpRequestEventTarget));

var RealXMLHttpRequest = window.XMLHttpRequest;
var XHRMock$1 = /** @class */ (function () {
    function XHRMock() {
        this.RealXMLHttpRequest = RealXMLHttpRequest;
    }
    XHRMock.prototype.setup = function () {
        // @ts-ignore: https://github.com/jameslnewell/xhr-mock/issues/45
        window.XMLHttpRequest = MockXMLHttpRequest;
        this.reset();
        return this;
    };
    XHRMock.prototype.teardown = function () {
        this.reset();
        window.XMLHttpRequest = RealXMLHttpRequest;
        return this;
    };
    XHRMock.prototype.reset = function () {
        MockXMLHttpRequest.removeAllHandlers();
        return this;
    };
    XHRMock.prototype.error = function (callback) {
        MockXMLHttpRequest.errorCallback = callback;
        return this;
    };
    XHRMock.prototype.mock = function (fnOrMethod, url, mock) {
        console.warn('xhr-mock: XHRMock.mock() has been deprecated. Use XHRMock.use() instead.');
        if (typeof fnOrMethod === 'string' &&
            (typeof url === 'string' || url instanceof RegExp) &&
            mock !== undefined) {
            return this.use(fnOrMethod, url, mock);
        }
        else if (typeof fnOrMethod === 'function') {
            return this.use(fnOrMethod);
        }
        else {
            throw new Error('xhr-mock: Invalid handler.');
        }
    };
    XHRMock.prototype.use = function (fnOrMethod, url, mock) {
        var fn;
        if (typeof fnOrMethod === 'string' &&
            (typeof url === 'string' || url instanceof RegExp) &&
            mock !== undefined) {
            fn = createMockFunction(fnOrMethod, url, mock);
        }
        else if (typeof fnOrMethod === 'function') {
            fn = fnOrMethod;
        }
        else {
            throw new Error('xhr-mock: Invalid handler.');
        }
        MockXMLHttpRequest.addHandler(fn);
        return this;
    };
    XHRMock.prototype.get = function (url, mock) {
        return this.use('GET', url, mock);
    };
    XHRMock.prototype.post = function (url, mock) {
        return this.use('POST', url, mock);
    };
    XHRMock.prototype.put = function (url, mock) {
        return this.use('PUT', url, mock);
    };
    XHRMock.prototype.patch = function (url, mock) {
        return this.use('PATCH', url, mock);
    };
    XHRMock.prototype["delete"] = function (url, mock) {
        return this.use('DELETE', url, mock);
    };
    return XHRMock;
}());
// I'm only using a class so I can make use make use of TS' method overrides
var XHRMock$2 = new XHRMock$1();

return XHRMock$2;

})));