UNPKG

@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
(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