@softchef/cdk-iot-device-management
Version:
IoT device management is composed of things, thing types, thing groups, jobs, files API services. The constructs can be used independently, that are based on full-managed service to create an API Gateway & Lambda function.
1,572 lines (1,328 loc) • 797 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.nise = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
// cache a reference to setTimeout, so that our reference won't be stubbed out
// when using fake timers and errors will still get logged
// https://github.com/cjohansen/Sinon.JS/issues/381
var realSetTimeout = setTimeout;
function configureLogger(config) {
// eslint-disable-next-line no-param-reassign
config = config || {};
// Function which prints errors.
if (!config.hasOwnProperty("logger")) {
// eslint-disable-next-line no-empty-function
config.logger = function() {};
}
// When set to true, any errors logged will be thrown immediately;
// If set to false, the errors will be thrown in separate execution frame.
if (!config.hasOwnProperty("useImmediateExceptions")) {
config.useImmediateExceptions = true;
}
// wrap realSetTimeout with something we can stub in tests
if (!config.hasOwnProperty("setTimeout")) {
config.setTimeout = realSetTimeout;
}
return function logError(label, e) {
var msg = `${label} threw exception: `;
var err = {
name: e.name || label,
message: e.message || e.toString(),
stack: e.stack
};
function throwLoggedError() {
err.message = msg + err.message;
throw err;
}
config.logger(`${msg}[${err.name}] ${err.message}`);
if (err.stack) {
config.logger(err.stack);
}
if (config.useImmediateExceptions) {
throwLoggedError();
} else {
config.setTimeout(throwLoggedError, 0);
}
};
}
module.exports = configureLogger;
},{}],2:[function(require,module,exports){
"use strict";
var Event = require("./event");
function CustomEvent(type, customData, target) {
this.initEvent(type, false, false, target);
this.detail = customData.detail || null;
}
CustomEvent.prototype = new Event();
CustomEvent.prototype.constructor = CustomEvent;
module.exports = CustomEvent;
},{"./event":4}],3:[function(require,module,exports){
"use strict";
function flattenOptions(options) {
if (options !== Object(options)) {
return {
capture: Boolean(options),
once: false,
passive: false
};
}
return {
capture: Boolean(options.capture),
once: Boolean(options.once),
passive: Boolean(options.passive)
};
}
function not(fn) {
return function() {
return !fn.apply(this, arguments);
};
}
function hasListenerFilter(listener, capture) {
return function(listenerSpec) {
return (
listenerSpec.capture === capture &&
listenerSpec.listener === listener
);
};
}
var EventTarget = {
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
addEventListener: function addEventListener(
event,
listener,
providedOptions
) {
// 3. Let capture, passive, and once be the result of flattening more options.
// Flatten property before executing step 2,
// feture detection is usually based on registering handler with options object,
// that has getter defined
// addEventListener("load", () => {}, {
// get once() { supportsOnce = true; }
// });
var options = flattenOptions(providedOptions);
// 2. If callback is null, then return.
if (listener === null || listener === undefined) {
return;
}
this.eventListeners = this.eventListeners || {};
this.eventListeners[event] = this.eventListeners[event] || [];
// 4. If context object’s associated list of event listener
// does not contain an event listener whose type is type,
// callback is callback, and capture is capture, then append
// a new event listener to it, whose type is type, callback is
// callback, capture is capture, passive is passive, and once is once.
if (
!this.eventListeners[event].some(
hasListenerFilter(listener, options.capture)
)
) {
this.eventListeners[event].push({
listener: listener,
capture: options.capture,
once: options.once
});
}
},
// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
removeEventListener: function removeEventListener(
event,
listener,
providedOptions
) {
if (!this.eventListeners || !this.eventListeners[event]) {
return;
}
// 2. Let capture be the result of flattening options.
var options = flattenOptions(providedOptions);
// 3. If there is an event listener in the associated list of
// event listeners whose type is type, callback is callback,
// and capture is capture, then set that event listener’s
// removed to true and remove it from the associated list of event listeners.
this.eventListeners[event] = this.eventListeners[event].filter(
not(hasListenerFilter(listener, options.capture))
);
},
dispatchEvent: function dispatchEvent(event) {
if (!this.eventListeners || !this.eventListeners[event.type]) {
return Boolean(event.defaultPrevented);
}
var self = this;
var type = event.type;
var listeners = self.eventListeners[type];
// Remove listeners, that should be dispatched once
// before running dispatch loop to avoid nested dispatch issues
self.eventListeners[type] = listeners.filter(function(listenerSpec) {
return !listenerSpec.once;
});
listeners.forEach(function(listenerSpec) {
var listener = listenerSpec.listener;
if (typeof listener === "function") {
listener.call(self, event);
} else {
listener.handleEvent(event);
}
});
return Boolean(event.defaultPrevented);
}
};
module.exports = EventTarget;
},{}],4:[function(require,module,exports){
"use strict";
function Event(type, bubbles, cancelable, target) {
this.initEvent(type, bubbles, cancelable, target);
}
Event.prototype = {
initEvent: function(type, bubbles, cancelable, target) {
this.type = type;
this.bubbles = bubbles;
this.cancelable = cancelable;
this.target = target;
this.currentTarget = target;
},
// eslint-disable-next-line no-empty-function
stopPropagation: function() {},
preventDefault: function() {
this.defaultPrevented = true;
}
};
module.exports = Event;
},{}],5:[function(require,module,exports){
"use strict";
module.exports = {
Event: require("./event"),
ProgressEvent: require("./progress-event"),
CustomEvent: require("./custom-event"),
EventTarget: require("./event-target")
};
},{"./custom-event":2,"./event":4,"./event-target":3,"./progress-event":6}],6:[function(require,module,exports){
"use strict";
var Event = require("./event");
function ProgressEvent(type, progressEventRaw, target) {
this.initEvent(type, false, false, target);
this.loaded =
typeof progressEventRaw.loaded === "number"
? progressEventRaw.loaded
: null;
this.total =
typeof progressEventRaw.total === "number"
? progressEventRaw.total
: null;
this.lengthComputable = Boolean(progressEventRaw.total);
}
ProgressEvent.prototype = new Event();
ProgressEvent.prototype.constructor = ProgressEvent;
module.exports = ProgressEvent;
},{"./event":4}],7:[function(require,module,exports){
"use strict";
var FakeTimers = require("@sinonjs/fake-timers");
var fakeServer = require("./index");
// eslint-disable-next-line no-empty-function
function Server() {}
Server.prototype = fakeServer;
var fakeServerWithClock = new Server();
fakeServerWithClock.addRequest = function addRequest(xhr) {
if (xhr.async) {
if (typeof setTimeout.clock === "object") {
this.clock = setTimeout.clock;
} else {
this.clock = FakeTimers.install();
this.resetClock = true;
}
if (!this.longestTimeout) {
var clockSetTimeout = this.clock.setTimeout;
var clockSetInterval = this.clock.setInterval;
var server = this;
this.clock.setTimeout = function(fn, timeout) {
server.longestTimeout = Math.max(
timeout,
server.longestTimeout || 0
);
return clockSetTimeout.apply(this, arguments);
};
this.clock.setInterval = function(fn, timeout) {
server.longestTimeout = Math.max(
timeout,
server.longestTimeout || 0
);
return clockSetInterval.apply(this, arguments);
};
}
}
return fakeServer.addRequest.call(this, xhr);
};
fakeServerWithClock.respond = function respond() {
var returnVal = fakeServer.respond.apply(this, arguments);
if (this.clock) {
this.clock.tick(this.longestTimeout || 0);
this.longestTimeout = 0;
if (this.resetClock) {
this.clock.uninstall();
this.resetClock = false;
}
}
return returnVal;
};
fakeServerWithClock.restore = function restore() {
if (this.clock) {
this.clock.uninstall();
}
return fakeServer.restore.apply(this, arguments);
};
module.exports = fakeServerWithClock;
},{"./index":8,"@sinonjs/fake-timers":31}],8:[function(require,module,exports){
"use strict";
var fakeXhr = require("../fake-xhr");
var push = [].push;
var log = require("./log");
var configureLogError = require("../configure-logger");
var pathToRegexp = require("path-to-regexp");
var supportsArrayBuffer = typeof ArrayBuffer !== "undefined";
function responseArray(handler) {
var response = handler;
if (Object.prototype.toString.call(handler) !== "[object Array]") {
response = [200, {}, handler];
}
if (typeof response[2] !== "string") {
if (!supportsArrayBuffer) {
throw new TypeError(
`Fake server response body should be a string, but was ${typeof response[2]}`
);
} else if (!(response[2] instanceof ArrayBuffer)) {
throw new TypeError(
`Fake server response body should be a string or ArrayBuffer, but was ${typeof response[2]}`
);
}
}
return response;
}
function getDefaultWindowLocation() {
var winloc = {
hostname: "localhost",
port: process.env.PORT || 80,
protocol: "http:"
};
winloc.host =
winloc.hostname +
(String(winloc.port) === "80" ? "" : `:${winloc.port}`);
return winloc;
}
function getWindowLocation() {
if (typeof window === "undefined") {
// Fallback
return getDefaultWindowLocation();
}
if (typeof window.location !== "undefined") {
// Browsers place location on window
return window.location;
}
if (
typeof window.window !== "undefined" &&
typeof window.window.location !== "undefined"
) {
// React Native on Android places location on window.window
return window.window.location;
}
return getDefaultWindowLocation();
}
function matchOne(response, reqMethod, reqUrl) {
var rmeth = response.method;
var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase();
var url = response.url;
var matchUrl =
!url ||
url === reqUrl ||
(typeof url.test === "function" && url.test(reqUrl)) ||
(typeof url === "function" && url(reqUrl) === true);
return matchMethod && matchUrl;
}
function match(response, request) {
var wloc = getWindowLocation();
var rCurrLoc = new RegExp(`^${wloc.protocol}//${wloc.host}/`);
var requestUrl = request.url;
if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
requestUrl = requestUrl.replace(rCurrLoc, "/");
}
if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
if (typeof response.response === "function") {
var ru = response.url;
var args = [request].concat(
ru && typeof ru.exec === "function"
? ru.exec(requestUrl).slice(1)
: []
);
return response.response.apply(response, args);
}
return true;
}
return false;
}
function incrementRequestCount() {
var count = ++this.requestCount;
this.requested = true;
this.requestedOnce = count === 1;
this.requestedTwice = count === 2;
this.requestedThrice = count === 3;
this.firstRequest = this.getRequest(0);
this.secondRequest = this.getRequest(1);
this.thirdRequest = this.getRequest(2);
this.lastRequest = this.getRequest(count - 1);
}
var fakeServer = {
create: function(config) {
var server = Object.create(this);
server.configure(config);
this.xhr = fakeXhr.useFakeXMLHttpRequest();
server.requests = [];
server.requestCount = 0;
server.queue = [];
server.responses = [];
this.xhr.onCreate = function(xhrObj) {
xhrObj.unsafeHeadersEnabled = function() {
return !(server.unsafeHeadersEnabled === false);
};
server.addRequest(xhrObj);
};
return server;
},
configure: function(config) {
var self = this;
var allowlist = {
autoRespond: true,
autoRespondAfter: true,
respondImmediately: true,
fakeHTTPMethods: true,
logger: true,
unsafeHeadersEnabled: true
};
// eslint-disable-next-line no-param-reassign
config = config || {};
Object.keys(config).forEach(function(setting) {
if (setting in allowlist) {
self[setting] = config[setting];
}
});
self.logError = configureLogError(config);
},
addRequest: function addRequest(xhrObj) {
var server = this;
push.call(this.requests, xhrObj);
incrementRequestCount.call(this);
xhrObj.onSend = function() {
server.handleRequest(this);
if (server.respondImmediately) {
server.respond();
} else if (server.autoRespond && !server.responding) {
setTimeout(function() {
server.responding = false;
server.respond();
}, server.autoRespondAfter || 10);
server.responding = true;
}
};
},
getHTTPMethod: function getHTTPMethod(request) {
if (this.fakeHTTPMethods && /post/i.test(request.method)) {
var matches = (request.requestBody || "").match(
/_method=([^\b;]+)/
);
return matches ? matches[1] : request.method;
}
return request.method;
},
handleRequest: function handleRequest(xhr) {
if (xhr.async) {
push.call(this.queue, xhr);
} else {
this.processRequest(xhr);
}
},
logger: function() {
// no-op; override via configure()
},
logError: configureLogError({}),
log: log,
respondWith: function respondWith(method, url, body) {
if (arguments.length === 1 && typeof method !== "function") {
this.response = responseArray(method);
return;
}
if (arguments.length === 1) {
// eslint-disable-next-line no-param-reassign
body = method;
// eslint-disable-next-line no-param-reassign
url = method = null;
}
if (arguments.length === 2) {
// eslint-disable-next-line no-param-reassign
body = url;
// eslint-disable-next-line no-param-reassign
url = method;
// eslint-disable-next-line no-param-reassign
method = null;
}
// Escape port number to prevent "named" parameters in 'path-to-regexp' module
if (typeof url === "string" && url !== "" && /:[0-9]+\//.test(url)) {
var m = url.match(/^(https?:\/\/.*?):([0-9]+\/.*)$/);
// eslint-disable-next-line no-param-reassign
url = `${m[1]}\\:${m[2]}`;
}
push.call(this.responses, {
method: method,
url:
typeof url === "string" && url !== "" ? pathToRegexp(url) : url,
response: typeof body === "function" ? body : responseArray(body)
});
},
respond: function respond() {
if (arguments.length > 0) {
this.respondWith.apply(this, arguments);
}
var queue = this.queue || [];
var requests = queue.splice(0, queue.length);
var self = this;
requests.forEach(function(request) {
self.processRequest(request);
});
},
respondAll: function respondAll() {
if (this.respondImmediately) {
return;
}
this.queue = this.requests.slice(0);
var request;
while ((request = this.queue.shift())) {
this.processRequest(request);
}
},
processRequest: function processRequest(request) {
try {
if (request.aborted) {
return;
}
var response = this.response || [404, {}, ""];
if (this.responses) {
for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
if (match.call(this, this.responses[i], request)) {
response = this.responses[i].response;
break;
}
}
}
if (request.readyState !== 4) {
this.log(response, request);
request.respond(response[0], response[1], response[2]);
}
} catch (e) {
this.logError("Fake server request processing", e);
}
},
restore: function restore() {
return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
},
getRequest: function getRequest(index) {
return this.requests[index] || null;
},
reset: function reset() {
this.resetBehavior();
this.resetHistory();
},
resetBehavior: function resetBehavior() {
this.responses.length = this.queue.length = 0;
},
resetHistory: function resetHistory() {
this.requests.length = this.requestCount = 0;
this.requestedOnce = this.requestedTwice = this.requestedThrice = this.requested = false;
this.firstRequest = this.secondRequest = this.thirdRequest = this.lastRequest = null;
}
};
module.exports = fakeServer;
},{"../configure-logger":1,"../fake-xhr":11,"./log":9,"path-to-regexp":37}],9:[function(require,module,exports){
"use strict";
var inspect = require("util").inspect;
function log(response, request) {
var str;
str = `Request:\n${inspect(request)}\n\n`;
str += `Response:\n${inspect(response)}\n\n`;
/* istanbul ignore else: when this.logger is not a function, it can't be called */
if (typeof this.logger === "function") {
this.logger(str);
}
}
module.exports = log;
},{"util":41}],10:[function(require,module,exports){
"use strict";
exports.isSupported = (function() {
try {
return Boolean(new Blob());
} catch (e) {
return false;
}
})();
},{}],11:[function(require,module,exports){
"use strict";
var GlobalTextEncoder =
typeof TextEncoder !== "undefined"
? TextEncoder
: require("@sinonjs/text-encoding").TextEncoder;
var globalObject = require("@sinonjs/commons").global;
var configureLogError = require("../configure-logger");
var sinonEvent = require("../event");
var extend = require("just-extend");
var supportsProgress = typeof ProgressEvent !== "undefined";
var supportsCustomEvent = typeof CustomEvent !== "undefined";
var supportsFormData = typeof FormData !== "undefined";
var supportsArrayBuffer = typeof ArrayBuffer !== "undefined";
var supportsBlob = require("./blob").isSupported;
function getWorkingXHR(globalScope) {
var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined";
if (supportsXHR) {
return globalScope.XMLHttpRequest;
}
var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined";
if (supportsActiveX) {
return function() {
return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0");
};
}
return false;
}
// Ref: https://fetch.spec.whatwg.org/#forbidden-header-name
var unsafeHeaders = {
"Accept-Charset": true,
"Access-Control-Request-Headers": true,
"Access-Control-Request-Method": true,
"Accept-Encoding": true,
Connection: true,
"Content-Length": true,
Cookie: true,
Cookie2: true,
"Content-Transfer-Encoding": true,
Date: true,
DNT: true,
Expect: true,
Host: true,
"Keep-Alive": true,
Origin: true,
Referer: true,
TE: true,
Trailer: true,
"Transfer-Encoding": true,
Upgrade: true,
"User-Agent": true,
Via: true
};
function EventTargetHandler() {
var self = this;
var events = [
"loadstart",
"progress",
"abort",
"error",
"load",
"timeout",
"loadend"
];
function addEventListener(eventName) {
self.addEventListener(eventName, function(event) {
var listener = self[`on${eventName}`];
if (listener && typeof listener === "function") {
listener.call(this, event);
}
});
}
events.forEach(addEventListener);
}
EventTargetHandler.prototype = sinonEvent.EventTarget;
function normalizeHeaderValue(value) {
// Ref: https://fetch.spec.whatwg.org/#http-whitespace-bytes
/*eslint no-control-regex: "off"*/
return value.replace(/^[\x09\x0A\x0D\x20]+|[\x09\x0A\x0D\x20]+$/g, "");
}
function getHeader(headers, header) {
var foundHeader = Object.keys(headers).filter(function(h) {
return h.toLowerCase() === header.toLowerCase();
});
return foundHeader[0] || null;
}
function excludeSetCookie2Header(header) {
return !/^Set-Cookie2?$/i.test(header);
}
function verifyResponseBodyType(body, responseType) {
var error = null;
var isString = typeof body === "string";
if (responseType === "arraybuffer") {
if (!isString && !(body instanceof ArrayBuffer)) {
error = new Error(
`Attempted to respond to fake XMLHttpRequest with ${body}, which is not a string or ArrayBuffer.`
);
error.name = "InvalidBodyException";
}
} else if (responseType === "blob") {
if (
!isString &&
!(body instanceof ArrayBuffer) &&
supportsBlob &&
!(body instanceof Blob)
) {
error = new Error(
`Attempted to respond to fake XMLHttpRequest with ${body}, which is not a string, ArrayBuffer, or Blob.`
);
error.name = "InvalidBodyException";
}
} else if (!isString) {
error = new Error(
`Attempted to respond to fake XMLHttpRequest with ${body}, which is not a string.`
);
error.name = "InvalidBodyException";
}
if (error) {
throw error;
}
}
function convertToArrayBuffer(body, encoding) {
if (body instanceof ArrayBuffer) {
return body;
}
return new GlobalTextEncoder(encoding || "utf-8").encode(body).buffer;
}
function isXmlContentType(contentType) {
return (
!contentType ||
/(text\/xml)|(application\/xml)|(\+xml)/.test(contentType)
);
}
function clearResponse(xhr) {
if (xhr.responseType === "" || xhr.responseType === "text") {
xhr.response = xhr.responseText = "";
} else {
xhr.response = xhr.responseText = null;
}
xhr.responseXML = null;
}
function fakeXMLHttpRequestFor(globalScope) {
var isReactNative =
globalScope.navigator &&
globalScope.navigator.product === "ReactNative";
var sinonXhr = { XMLHttpRequest: globalScope.XMLHttpRequest };
sinonXhr.GlobalXMLHttpRequest = globalScope.XMLHttpRequest;
sinonXhr.GlobalActiveXObject = globalScope.ActiveXObject;
sinonXhr.supportsActiveX =
typeof sinonXhr.GlobalActiveXObject !== "undefined";
sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined";
sinonXhr.workingXHR = getWorkingXHR(globalScope);
sinonXhr.supportsTimeout =
sinonXhr.supportsXHR &&
"timeout" in new sinonXhr.GlobalXMLHttpRequest();
sinonXhr.supportsCORS =
isReactNative ||
(sinonXhr.supportsXHR &&
"withCredentials" in new sinonXhr.GlobalXMLHttpRequest());
// Note that for FakeXMLHttpRequest to work pre ES5
// we lose some of the alignment with the spec.
// To ensure as close a match as possible,
// set responseType before calling open, send or respond;
function FakeXMLHttpRequest(config) {
EventTargetHandler.call(this);
this.readyState = FakeXMLHttpRequest.UNSENT;
this.requestHeaders = {};
this.requestBody = null;
this.status = 0;
this.statusText = "";
this.upload = new EventTargetHandler();
this.responseType = "";
this.response = "";
this.logError = configureLogError(config);
if (sinonXhr.supportsTimeout) {
this.timeout = 0;
}
if (sinonXhr.supportsCORS) {
this.withCredentials = false;
}
if (typeof FakeXMLHttpRequest.onCreate === "function") {
FakeXMLHttpRequest.onCreate(this);
}
}
function verifyState(xhr) {
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
throw new Error("INVALID_STATE_ERR");
}
if (xhr.sendFlag) {
throw new Error("INVALID_STATE_ERR");
}
}
// largest arity in XHR is 5 - XHR#open
var apply = function(obj, method, args) {
switch (args.length) {
case 0:
return obj[method]();
case 1:
return obj[method](args[0]);
case 2:
return obj[method](args[0], args[1]);
case 3:
return obj[method](args[0], args[1], args[2]);
case 4:
return obj[method](args[0], args[1], args[2], args[3]);
case 5:
return obj[method](args[0], args[1], args[2], args[3], args[4]);
default:
throw new Error("Unhandled case");
}
};
FakeXMLHttpRequest.filters = [];
FakeXMLHttpRequest.addFilter = function addFilter(fn) {
this.filters.push(fn);
};
FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap
[
"open",
"setRequestHeader",
"abort",
"getResponseHeader",
"getAllResponseHeaders",
"addEventListener",
"overrideMimeType",
"removeEventListener"
].forEach(function(method) {
fakeXhr[method] = function() {
return apply(xhr, method, arguments);
};
});
fakeXhr.send = function() {
// Ref: https://xhr.spec.whatwg.org/#the-responsetype-attribute
if (xhr.responseType !== fakeXhr.responseType) {
xhr.responseType = fakeXhr.responseType;
}
return apply(xhr, "send", arguments);
};
var copyAttrs = function(args) {
args.forEach(function(attr) {
fakeXhr[attr] = xhr[attr];
});
};
var stateChangeStart = function() {
fakeXhr.readyState = xhr.readyState;
if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
copyAttrs(["status", "statusText"]);
}
if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
copyAttrs(["response"]);
if (xhr.responseType === "" || xhr.responseType === "text") {
copyAttrs(["responseText"]);
}
}
if (
xhr.readyState === FakeXMLHttpRequest.DONE &&
(xhr.responseType === "" || xhr.responseType === "document")
) {
copyAttrs(["responseXML"]);
}
};
var stateChangeEnd = function() {
if (fakeXhr.onreadystatechange) {
// eslint-disable-next-line no-useless-call
fakeXhr.onreadystatechange.call(fakeXhr, {
target: fakeXhr,
currentTarget: fakeXhr
});
}
};
var stateChange = function stateChange() {
stateChangeStart();
stateChangeEnd();
};
if (xhr.addEventListener) {
xhr.addEventListener("readystatechange", stateChangeStart);
Object.keys(fakeXhr.eventListeners).forEach(function(event) {
/*eslint-disable no-loop-func*/
fakeXhr.eventListeners[event].forEach(function(handler) {
xhr.addEventListener(event, handler.listener, {
capture: handler.capture,
once: handler.once
});
});
/*eslint-enable no-loop-func*/
});
xhr.addEventListener("readystatechange", stateChangeEnd);
} else {
xhr.onreadystatechange = stateChange;
}
apply(xhr, "open", xhrArgs);
};
FakeXMLHttpRequest.useFilters = false;
function verifyRequestOpened(xhr) {
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
throw new Error(`INVALID_STATE_ERR - ${xhr.readyState}`);
}
}
function verifyRequestSent(xhr) {
if (xhr.readyState === FakeXMLHttpRequest.DONE) {
throw new Error("Request done");
}
}
function verifyHeadersReceived(xhr) {
if (
xhr.async &&
xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED
) {
throw new Error("No headers received");
}
}
function convertResponseBody(responseType, contentType, body) {
if (responseType === "" || responseType === "text") {
return body;
} else if (supportsArrayBuffer && responseType === "arraybuffer") {
return convertToArrayBuffer(body);
} else if (responseType === "json") {
try {
return JSON.parse(body);
} catch (e) {
// Return parsing failure as null
return null;
}
} else if (supportsBlob && responseType === "blob") {
if (body instanceof Blob) {
return body;
}
var blobOptions = {};
if (contentType) {
blobOptions.type = contentType;
}
return new Blob([convertToArrayBuffer(body)], blobOptions);
} else if (responseType === "document") {
if (isXmlContentType(contentType)) {
return FakeXMLHttpRequest.parseXML(body);
}
return null;
}
throw new Error(`Invalid responseType ${responseType}`);
}
/**
* Steps to follow when there is an error, according to:
* https://xhr.spec.whatwg.org/#request-error-steps
*/
function requestErrorSteps(xhr) {
clearResponse(xhr);
xhr.errorFlag = true;
xhr.requestHeaders = {};
xhr.responseHeaders = {};
if (
xhr.readyState !== FakeXMLHttpRequest.UNSENT &&
xhr.sendFlag &&
xhr.readyState !== FakeXMLHttpRequest.DONE
) {
xhr.readyStateChange(FakeXMLHttpRequest.DONE);
xhr.sendFlag = false;
}
}
FakeXMLHttpRequest.parseXML = function parseXML(text) {
// Treat empty string as parsing failure
if (text !== "") {
try {
if (typeof DOMParser !== "undefined") {
var parser = new DOMParser();
var parsererrorNS = "";
try {
var parsererrors = parser
.parseFromString("INVALID", "text/xml")
.getElementsByTagName("parsererror");
if (parsererrors.length) {
parsererrorNS = parsererrors[0].namespaceURI;
}
} catch (e) {
// passing invalid XML makes IE11 throw
// so no namespace needs to be determined
}
var result;
try {
result = parser.parseFromString(text, "text/xml");
} catch (err) {
return null;
}
return result.getElementsByTagNameNS(
parsererrorNS,
"parsererror"
).length
? null
: result;
}
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(text);
return xmlDoc.parseError.errorCode !== 0 ? null : xmlDoc;
} catch (e) {
// Unable to parse XML - no biggie
}
}
return null;
};
FakeXMLHttpRequest.statusCodes = {
100: "Continue",
101: "Switching Protocols",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
300: "Multiple Choice",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
307: "Temporary Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
422: "Unprocessable Entity",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported"
};
extend(FakeXMLHttpRequest.prototype, sinonEvent.EventTarget, {
async: true,
open: function open(method, url, async, username, password) {
this.method = method;
this.url = url;
this.async = typeof async === "boolean" ? async : true;
this.username = username;
this.password = password;
clearResponse(this);
this.requestHeaders = {};
this.sendFlag = false;
if (FakeXMLHttpRequest.useFilters === true) {
var xhrArgs = arguments;
var defake = FakeXMLHttpRequest.filters.some(function(filter) {
return filter.apply(this, xhrArgs);
});
if (defake) {
FakeXMLHttpRequest.defake(this, arguments);
return;
}
}
this.readyStateChange(FakeXMLHttpRequest.OPENED);
},
readyStateChange: function readyStateChange(state) {
this.readyState = state;
var readyStateChangeEvent = new sinonEvent.Event(
"readystatechange",
false,
false,
this
);
if (typeof this.onreadystatechange === "function") {
try {
this.onreadystatechange(readyStateChangeEvent);
} catch (e) {
this.logError("Fake XHR onreadystatechange handler", e);
}
}
if (this.readyState !== FakeXMLHttpRequest.DONE) {
this.dispatchEvent(readyStateChangeEvent);
} else {
var event, progress;
if (this.timedOut || this.aborted || this.status === 0) {
progress = { loaded: 0, total: 0 };
event =
(this.timedOut && "timeout") ||
(this.aborted && "abort") ||
"error";
} else {
progress = { loaded: 100, total: 100 };
event = "load";
}
if (supportsProgress) {
this.upload.dispatchEvent(
new sinonEvent.ProgressEvent("progress", progress, this)
);
this.upload.dispatchEvent(
new sinonEvent.ProgressEvent(event, progress, this)
);
this.upload.dispatchEvent(
new sinonEvent.ProgressEvent("loadend", progress, this)
);
}
this.dispatchEvent(
new sinonEvent.ProgressEvent("progress", progress, this)
);
this.dispatchEvent(
new sinonEvent.ProgressEvent(event, progress, this)
);
this.dispatchEvent(readyStateChangeEvent);
this.dispatchEvent(
new sinonEvent.ProgressEvent("loadend", progress, this)
);
}
},
// Ref https://xhr.spec.whatwg.org/#the-setrequestheader()-method
setRequestHeader: function setRequestHeader(header, value) {
if (typeof value !== "string") {
throw new TypeError(
`By RFC7230, section 3.2.4, header values should be strings. Got ${typeof value}`
);
}
verifyState(this);
var checkUnsafeHeaders = true;
if (typeof this.unsafeHeadersEnabled === "function") {
checkUnsafeHeaders = this.unsafeHeadersEnabled();
}
if (
checkUnsafeHeaders &&
(getHeader(unsafeHeaders, header) !== null ||
/^(Sec-|Proxy-)/i.test(header))
) {
throw new Error(
// eslint-disable-next-line quotes
`Refused to set unsafe header "${header}"`
);
}
// eslint-disable-next-line no-param-reassign
value = normalizeHeaderValue(value);
var existingHeader = getHeader(this.requestHeaders, header);
if (existingHeader) {
this.requestHeaders[existingHeader] += `, ${value}`;
} else {
this.requestHeaders[header] = value;
}
},
setStatus: function setStatus(status) {
var sanitizedStatus = typeof status === "number" ? status : 200;
verifyRequestOpened(this);
this.status = sanitizedStatus;
this.statusText = FakeXMLHttpRequest.statusCodes[sanitizedStatus];
},
// Helps testing
setResponseHeaders: function setResponseHeaders(headers) {
verifyRequestOpened(this);
var responseHeaders = (this.responseHeaders = {});
Object.keys(headers).forEach(function(header) {
responseHeaders[header] = headers[header];
});
if (this.async) {
this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
} else {
this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
}
},
// Currently treats ALL data as a DOMString (i.e. no Document)
send: function send(data) {
verifyState(this);
if (!/^(head)$/i.test(this.method)) {
var contentType = getHeader(
this.requestHeaders,
"Content-Type"
);
if (this.requestHeaders[contentType]) {
var value = this.requestHeaders[contentType].split(";");
this.requestHeaders[
contentType
] = `${value[0]};charset=utf-8`;
} else if (supportsFormData && !(data instanceof FormData)) {
this.requestHeaders["Content-Type"] =
"text/plain;charset=utf-8";
}
this.requestBody = data;
}
this.errorFlag = false;
this.sendFlag = this.async;
clearResponse(this);
if (typeof this.onSend === "function") {
this.onSend(this);
}
// Only listen if setInterval and Date are a stubbed.
if (
sinonXhr.supportsTimeout &&
typeof setInterval.clock === "object" &&
typeof Date.clock === "object"
) {
var initiatedTime = Date.now();
var self = this;
// Listen to any possible tick by fake timers and check to see if timeout has
// been exceeded. It's important to note that timeout can be changed while a request
// is in flight, so we must check anytime the end user forces a clock tick to make
// sure timeout hasn't changed.
// https://xhr.spec.whatwg.org/#dfnReturnLink-2
var clearIntervalId = setInterval(function() {
// Check if the readyState has been reset or is done. If this is the case, there
// should be no timeout. This will also prevent aborted requests and
// fakeServerWithClock from triggering unnecessary responses.
if (
self.readyState === FakeXMLHttpRequest.UNSENT ||
self.readyState === FakeXMLHttpRequest.DONE
) {
clearInterval(clearIntervalId);
} else if (
typeof self.timeout === "number" &&
self.timeout > 0
) {
if (Date.now() >= initiatedTime + self.timeout) {
self.triggerTimeout();
clearInterval(clearIntervalId);
}
}
}, 1);
}
this.dispatchEvent(
new sinonEvent.Event("loadstart", false, false, this)
);
},
abort: function abort() {
this.aborted = true;
requestErrorSteps(this);
this.readyState = FakeXMLHttpRequest.UNSENT;
},
error: function() {
clearResponse(this);
this.errorFlag = true;
this.requestHeaders = {};
this.responseHeaders = {};
this.readyStateChange(FakeXMLHttpRequest.DONE);
},
triggerTimeout: function triggerTimeout() {
if (sinonXhr.supportsTimeout) {
this.timedOut = true;
requestErrorSteps(this);
}
},
getResponseHeader: function getResponseHeader(header) {
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
return null;
}
if (/^Set-Cookie2?$/i.test(header)) {
return null;
}
// eslint-disable-next-line no-param-reassign
header = getHeader(this.responseHeaders, header);
return this.responseHeaders[header] || null;
},
getAllResponseHeaders: function getAllResponseHeaders() {
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
return "";
}
var responseHeaders = this.responseHeaders;
var headers = Object.keys(responseHeaders)
.filter(excludeSetCookie2Header)
.reduce(function(prev, header) {
var value = responseHeaders[header];
return `${prev}${header}: ${value}\r\n`;
}, "");
return headers;
},
setResponseBody: function setResponseBody(body) {
verifyRequestSent(this);
verifyHeadersReceived(this);
verifyResponseBodyType(body, this.responseType);
var contentType =
this.overriddenMimeType ||
this.getResponseHeader("Content-Type");
var isTextResponse =
this.responseType === "" || this.responseType === "text";
clearResponse(this);
if (this.async) {
var chunkSize = this.chunkSize || 10;
var index = 0;
do {
this.readyStateChange(FakeXMLHttpRequest.LOADING);
if (isTextResponse) {
this.responseText = this.response += body.substring(
index,
index + chunkSize
);
}
index += chunkSize;
} while (index < body.length);
}
this.response = convertResponseBody(
this.responseType,
contentType,
body
);
if (isTextResponse) {
this.responseText = this.response;
}
if (this.responseType === "document") {
this.responseXML = this.response;
} else if (
this.responseType === "" &&
isXmlContentType(contentType)
) {
this.responseXML = FakeXMLHttpRequest.parseXML(
this.responseText
);
}
this.readyStateChange(FakeXMLHttpRequest.DONE);
},
respond: function respond(status, headers, body) {
this.responseURL = this.url;
this.setStatus(status);
this.setResponseHeaders(headers || {});
this.setResponseBody(body || "");
},
uploadProgress: function uploadProgress(progressEventRaw) {
if (supportsProgress) {
this.upload.dispatchEvent(
new sinonEvent.ProgressEvent(
"progress",
progressEventRaw,
this.upload
)
);
}
},
downloadProgress: function downloadProgress(progressEventRaw) {
if (supportsProgress) {
this.dispatchEvent(
new sinonEvent.ProgressEvent(
"progress",
progressEventRaw,
this
)
);
}
},
uploadError: function uploadError(error) {
if (supportsCustomEvent) {
this.upload.dispatchEvent(
new sinonEvent.CustomEvent("error", { detail: error })
);
}
},
overrideMimeType: function overrideMimeType(type) {
if (this.readyState >= FakeXMLHttpRequest.LOADING) {
throw new Error("INVALID_STATE_ERR");
}
this.overriddenMimeType = type;
}
});
var states = {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
};
extend(FakeXMLHttpRequest, states);
extend(FakeXMLHttpRequest.prototype, states);
function useFakeXMLHttpRequest() {
FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
if (sinonXhr.suppo