UNPKG

@stacks/blockchain-api-client

Version:
1,623 lines (1,550 loc) 180 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.StacksBlockchainApiClient = {})); })(this, (function (exports) { function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } // settings & const const DEFAULT_HEADERS = { "Content-Type": "application/json", }; const PATH_PARAM_RE = /\{[^{}]+\}/g; /** Add custom parameters to Request object */ class CustomRequest extends Request { constructor(input, init) { super(input, init); // add custom parameters for (const key in init) { if (!(key in this)) { this[key] = init[key]; } } } } /** * Returns a cheap, non-cryptographically-secure random ID * Courtesy of @imranbarbhuiya (https://github.com/imranbarbhuiya) */ function randomID() { return Math.random().toString(36).slice(2, 11); } /** * Create an openapi-fetch client. * @type {import("./index.js").default} */ function createClient$1(clientOptions) { let { baseUrl = "", fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, headers: baseHeaders, ...baseOptions } = { ...clientOptions }; if (baseUrl.endsWith("/")) { baseUrl = baseUrl.substring(0, baseUrl.length - 1); } baseHeaders = mergeHeaders(DEFAULT_HEADERS, baseHeaders); const middlewares = []; /** * Per-request fetch (keeps settings created in createClient() * @param {T} url * @param {import('./index.js').FetchOptions<T>} fetchOptions */ async function coreFetch(schemaPath, fetchOptions) { const { fetch = baseFetch, headers, params = {}, parseAs = "json", querySerializer: requestQuerySerializer, bodySerializer = globalBodySerializer ?? defaultBodySerializer, ...init } = fetchOptions || {}; let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer); if (requestQuerySerializer) { querySerializer = typeof requestQuerySerializer === "function" ? requestQuerySerializer : createQuerySerializer({ ...(typeof globalQuerySerializer === "object" ? globalQuerySerializer : {}), ...requestQuerySerializer, }); } const requestInit = { redirect: "follow", ...baseOptions, ...init, headers: mergeHeaders(baseHeaders, headers, params.header), }; if (requestInit.body) { requestInit.body = bodySerializer(requestInit.body); // remove `Content-Type` if serialized body is FormData; browser will correctly set Content-Type & boundary expression if (requestInit.body instanceof FormData) { requestInit.headers.delete("Content-Type"); } } let id; let options; let request = new CustomRequest(createFinalURL(schemaPath, { baseUrl, params, querySerializer }), requestInit); if (middlewares.length) { id = randomID(); // middleware (request) options = Object.freeze({ baseUrl, fetch, parseAs, querySerializer, bodySerializer, }); for (const m of middlewares) { if (m && typeof m === "object" && typeof m.onRequest === "function") { const result = await m.onRequest({ request, schemaPath, params, options, id, }); if (result) { if (!(result instanceof Request)) { throw new Error("onRequest: must return new Request() when modifying the request"); } request = result; } } } } // fetch! let response = await fetch(request); // middleware (response) // execute in reverse-array order (first priority gets last transform) if (middlewares.length) { for (let i = middlewares.length - 1; i >= 0; i--) { const m = middlewares[i]; if (m && typeof m === "object" && typeof m.onResponse === "function") { const result = await m.onResponse({ request, response, schemaPath, params, options, id, }); if (result) { if (!(result instanceof Response)) { throw new Error("onResponse: must return new Response() when modifying the response"); } response = result; } } } } // handle empty content // note: we return `{}` because we want user truthy checks for `.data` or `.error` to succeed if (response.status === 204 || response.headers.get("Content-Length") === "0") { return response.ok ? { data: {}, response } : { error: {}, response }; } // parse response (falling back to .text() when necessary) if (response.ok) { // if "stream", skip parsing entirely if (parseAs === "stream") { return { data: response.body, response }; } return { data: await response[parseAs](), response }; } // handle errors let error = await response.text(); try { error = JSON.parse(error); // attempt to parse as JSON } catch { // noop } return { error, response }; } return { /** Call a GET endpoint */ GET(url, init) { return coreFetch(url, { ...init, method: "GET" }); }, /** Call a PUT endpoint */ PUT(url, init) { return coreFetch(url, { ...init, method: "PUT" }); }, /** Call a POST endpoint */ POST(url, init) { return coreFetch(url, { ...init, method: "POST" }); }, /** Call a DELETE endpoint */ DELETE(url, init) { return coreFetch(url, { ...init, method: "DELETE" }); }, /** Call a OPTIONS endpoint */ OPTIONS(url, init) { return coreFetch(url, { ...init, method: "OPTIONS" }); }, /** Call a HEAD endpoint */ HEAD(url, init) { return coreFetch(url, { ...init, method: "HEAD" }); }, /** Call a PATCH endpoint */ PATCH(url, init) { return coreFetch(url, { ...init, method: "PATCH" }); }, /** Call a TRACE endpoint */ TRACE(url, init) { return coreFetch(url, { ...init, method: "TRACE" }); }, /** Register middleware */ use(...middleware) { for (const m of middleware) { if (!m) { continue; } if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m)) { throw new Error("Middleware must be an object with one of `onRequest()` or `onResponse()`"); } middlewares.push(m); } }, /** Unregister middleware */ eject(...middleware) { for (const m of middleware) { const i = middlewares.indexOf(m); if (i !== -1) { middlewares.splice(i, 1); } } }, }; } // utils /** * Serialize primitive param values * @type {import("./index.js").serializePrimitiveParam} */ function serializePrimitiveParam(name, value, options) { if (value === undefined || value === null) { return ""; } if (typeof value === "object") { throw new Error( "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.", ); } return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`; } /** * Serialize object param (shallow only) * @type {import("./index.js").serializeObjectParam} */ function serializeObjectParam(name, value, options) { if (!value || typeof value !== "object") { return ""; } const values = []; const joiner = { simple: ",", label: ".", matrix: ";", }[options.style] || "&"; // explode: false if (options.style !== "deepObject" && options.explode === false) { for (const k in value) { values.push(k, options.allowReserved === true ? value[k] : encodeURIComponent(value[k])); } const final = values.join(","); // note: values are always joined by comma in explode: false (but joiner can prefix) switch (options.style) { case "form": { return `${name}=${final}`; } case "label": { return `.${final}`; } case "matrix": { return `;${name}=${final}`; } default: { return final; } } } // explode: true for (const k in value) { const finalName = options.style === "deepObject" ? `${name}[${k}]` : k; values.push(serializePrimitiveParam(finalName, value[k], options)); } const final = values.join(joiner); return options.style === "label" || options.style === "matrix" ? `${joiner}${final}` : final; } /** * Serialize array param (shallow only) * @type {import("./index.js").serializeArrayParam} */ function serializeArrayParam(name, value, options) { if (!Array.isArray(value)) { return ""; } // explode: false if (options.explode === false) { const joiner = { form: ",", spaceDelimited: "%20", pipeDelimited: "|" }[options.style] || ","; // note: for arrays, joiners vary wildly based on style + explode behavior const final = (options.allowReserved === true ? value : value.map((v) => encodeURIComponent(v))).join(joiner); switch (options.style) { case "simple": { return final; } case "label": { return `.${final}`; } case "matrix": { return `;${name}=${final}`; } // case "spaceDelimited": // case "pipeDelimited": default: { return `${name}=${final}`; } } } // explode: true const joiner = { simple: ",", label: ".", matrix: ";" }[options.style] || "&"; const values = []; for (const v of value) { if (options.style === "simple" || options.style === "label") { values.push(options.allowReserved === true ? v : encodeURIComponent(v)); } else { values.push(serializePrimitiveParam(name, v, options)); } } return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner); } /** * Serialize query params to string * @type {import("./index.js").createQuerySerializer} */ function createQuerySerializer(options) { return function querySerializer(queryParams) { const search = []; if (queryParams && typeof queryParams === "object") { for (const name in queryParams) { const value = queryParams[name]; if (value === undefined || value === null) { continue; } if (Array.isArray(value)) { search.push( serializeArrayParam(name, value, { style: "form", explode: true, ...options?.array, allowReserved: options?.allowReserved || false, }), ); continue; } if (typeof value === "object") { search.push( serializeObjectParam(name, value, { style: "deepObject", explode: true, ...options?.object, allowReserved: options?.allowReserved || false, }), ); continue; } search.push(serializePrimitiveParam(name, value, options)); } } return search.join("&"); }; } /** * Handle different OpenAPI 3.x serialization styles * @type {import("./index.js").defaultPathSerializer} * @see https://swagger.io/docs/specification/serialization/#path */ function defaultPathSerializer(pathname, pathParams) { let nextURL = pathname; for (const match of pathname.match(PATH_PARAM_RE) ?? []) { let name = match.substring(1, match.length - 1); let explode = false; let style = "simple"; if (name.endsWith("*")) { explode = true; name = name.substring(0, name.length - 1); } if (name.startsWith(".")) { style = "label"; name = name.substring(1); } else if (name.startsWith(";")) { style = "matrix"; name = name.substring(1); } if (!pathParams || pathParams[name] === undefined || pathParams[name] === null) { continue; } const value = pathParams[name]; if (Array.isArray(value)) { nextURL = nextURL.replace(match, serializeArrayParam(name, value, { style, explode })); continue; } if (typeof value === "object") { nextURL = nextURL.replace(match, serializeObjectParam(name, value, { style, explode })); continue; } if (style === "matrix") { nextURL = nextURL.replace(match, `;${serializePrimitiveParam(name, value)}`); continue; } nextURL = nextURL.replace(match, style === "label" ? `.${encodeURIComponent(value)}` : encodeURIComponent(value)); } return nextURL; } /** * Serialize body object to string * @type {import("./index.js").defaultBodySerializer} */ function defaultBodySerializer(body) { if (body instanceof FormData) { return body; } return JSON.stringify(body); } /** * Construct URL string from baseUrl and handle path and query params * @type {import("./index.js").createFinalURL} */ function createFinalURL(pathname, options) { let finalURL = `${options.baseUrl}${pathname}`; if (options.params?.path) { finalURL = defaultPathSerializer(finalURL, options.params.path); } let search = options.querySerializer(options.params.query ?? {}); if (search.startsWith("?")) { search = search.substring(1); } if (search) { finalURL += `?${search}`; } return finalURL; } /** * Merge headers a and b, with b taking priority * @type {import("./index.js").mergeHeaders} */ function mergeHeaders(...allHeaders) { const finalHeaders = new Headers(); for (const h of allHeaders) { if (!h || typeof h !== "object") { continue; } const iterator = h instanceof Headers ? h.entries() : Object.entries(h); for (const [k, v] of iterator) { if (v === null) { finalHeaders.delete(k); } else if (Array.isArray(v)) { for (const v2 of v) { finalHeaders.append(k, v2); } } else if (v !== undefined) { finalHeaders.set(k, v); } } } return finalHeaders; } var BASE_PATH = "https://api.mainnet.hiro.so"; const PACKET_TYPES = Object.create(null); // no Map = no polyfill PACKET_TYPES["open"] = "0"; PACKET_TYPES["close"] = "1"; PACKET_TYPES["ping"] = "2"; PACKET_TYPES["pong"] = "3"; PACKET_TYPES["message"] = "4"; PACKET_TYPES["upgrade"] = "5"; PACKET_TYPES["noop"] = "6"; const PACKET_TYPES_REVERSE = Object.create(null); Object.keys(PACKET_TYPES).forEach((key) => { PACKET_TYPES_REVERSE[PACKET_TYPES[key]] = key; }); const ERROR_PACKET = { type: "error", data: "parser error" }; const withNativeBlob$1 = typeof Blob === "function" || (typeof Blob !== "undefined" && Object.prototype.toString.call(Blob) === "[object BlobConstructor]"); const withNativeArrayBuffer$2 = typeof ArrayBuffer === "function"; // ArrayBuffer.isView method is not defined in IE10 const isView$1 = (obj) => { return typeof ArrayBuffer.isView === "function" ? ArrayBuffer.isView(obj) : obj && obj.buffer instanceof ArrayBuffer; }; const encodePacket = ({ type, data }, supportsBinary, callback) => { if (withNativeBlob$1 && data instanceof Blob) { if (supportsBinary) { return callback(data); } else { return encodeBlobAsBase64(data, callback); } } else if (withNativeArrayBuffer$2 && (data instanceof ArrayBuffer || isView$1(data))) { if (supportsBinary) { return callback(data); } else { return encodeBlobAsBase64(new Blob([data]), callback); } } // plain string return callback(PACKET_TYPES[type] + (data || "")); }; const encodeBlobAsBase64 = (data, callback) => { const fileReader = new FileReader(); fileReader.onload = function () { const content = fileReader.result.split(",")[1]; callback("b" + (content || "")); }; return fileReader.readAsDataURL(data); }; function toArray(data) { if (data instanceof Uint8Array) { return data; } else if (data instanceof ArrayBuffer) { return new Uint8Array(data); } else { return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); } } let TEXT_ENCODER; function encodePacketToBinary(packet, callback) { if (withNativeBlob$1 && packet.data instanceof Blob) { return packet.data.arrayBuffer().then(toArray).then(callback); } else if (withNativeArrayBuffer$2 && (packet.data instanceof ArrayBuffer || isView$1(packet.data))) { return callback(toArray(packet.data)); } encodePacket(packet, false, (encoded) => { if (!TEXT_ENCODER) { TEXT_ENCODER = new TextEncoder(); } callback(TEXT_ENCODER.encode(encoded)); }); } // imported from https://github.com/socketio/base64-arraybuffer const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // Use a lookup table to find the index. const lookup$1 = typeof Uint8Array === 'undefined' ? [] : new Uint8Array(256); for (let i = 0; i < chars.length; i++) { lookup$1[chars.charCodeAt(i)] = i; } const decode$1 = (base64) => { let bufferLength = base64.length * 0.75, len = base64.length, i, p = 0, encoded1, encoded2, encoded3, encoded4; if (base64[base64.length - 1] === '=') { bufferLength--; if (base64[base64.length - 2] === '=') { bufferLength--; } } const arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer); for (i = 0; i < len; i += 4) { encoded1 = lookup$1[base64.charCodeAt(i)]; encoded2 = lookup$1[base64.charCodeAt(i + 1)]; encoded3 = lookup$1[base64.charCodeAt(i + 2)]; encoded4 = lookup$1[base64.charCodeAt(i + 3)]; bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return arraybuffer; }; const withNativeArrayBuffer$1 = typeof ArrayBuffer === "function"; const decodePacket = (encodedPacket, binaryType) => { if (typeof encodedPacket !== "string") { return { type: "message", data: mapBinary(encodedPacket, binaryType), }; } const type = encodedPacket.charAt(0); if (type === "b") { return { type: "message", data: decodeBase64Packet(encodedPacket.substring(1), binaryType), }; } const packetType = PACKET_TYPES_REVERSE[type]; if (!packetType) { return ERROR_PACKET; } return encodedPacket.length > 1 ? { type: PACKET_TYPES_REVERSE[type], data: encodedPacket.substring(1), } : { type: PACKET_TYPES_REVERSE[type], }; }; const decodeBase64Packet = (data, binaryType) => { if (withNativeArrayBuffer$1) { const decoded = decode$1(data); return mapBinary(decoded, binaryType); } else { return { base64: true, data }; // fallback for old browsers } }; const mapBinary = (data, binaryType) => { switch (binaryType) { case "blob": if (data instanceof Blob) { // from WebSocket + binaryType "blob" return data; } else { // from HTTP long-polling or WebTransport return new Blob([data]); } case "arraybuffer": default: if (data instanceof ArrayBuffer) { // from HTTP long-polling (base64) or WebSocket + binaryType "arraybuffer" return data; } else { // from WebTransport (Uint8Array) return data.buffer; } } }; const SEPARATOR = String.fromCharCode(30); // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text const encodePayload = (packets, callback) => { // some packets may be added to the array while encoding, so the initial length must be saved const length = packets.length; const encodedPackets = new Array(length); let count = 0; packets.forEach((packet, i) => { // force base64 encoding for binary packets encodePacket(packet, false, (encodedPacket) => { encodedPackets[i] = encodedPacket; if (++count === length) { callback(encodedPackets.join(SEPARATOR)); } }); }); }; const decodePayload = (encodedPayload, binaryType) => { const encodedPackets = encodedPayload.split(SEPARATOR); const packets = []; for (let i = 0; i < encodedPackets.length; i++) { const decodedPacket = decodePacket(encodedPackets[i], binaryType); packets.push(decodedPacket); if (decodedPacket.type === "error") { break; } } return packets; }; function createPacketEncoderStream() { return new TransformStream({ transform(packet, controller) { encodePacketToBinary(packet, (encodedPacket) => { const payloadLength = encodedPacket.length; let header; // inspired by the WebSocket format: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#decoding_payload_length if (payloadLength < 126) { header = new Uint8Array(1); new DataView(header.buffer).setUint8(0, payloadLength); } else if (payloadLength < 65536) { header = new Uint8Array(3); const view = new DataView(header.buffer); view.setUint8(0, 126); view.setUint16(1, payloadLength); } else { header = new Uint8Array(9); const view = new DataView(header.buffer); view.setUint8(0, 127); view.setBigUint64(1, BigInt(payloadLength)); } // first bit indicates whether the payload is plain text (0) or binary (1) if (packet.data && typeof packet.data !== "string") { header[0] |= 0x80; } controller.enqueue(header); controller.enqueue(encodedPacket); }); }, }); } let TEXT_DECODER; function totalLength(chunks) { return chunks.reduce((acc, chunk) => acc + chunk.length, 0); } function concatChunks(chunks, size) { if (chunks[0].length === size) { return chunks.shift(); } const buffer = new Uint8Array(size); let j = 0; for (let i = 0; i < size; i++) { buffer[i] = chunks[0][j++]; if (j === chunks[0].length) { chunks.shift(); j = 0; } } if (chunks.length && j < chunks[0].length) { chunks[0] = chunks[0].slice(j); } return buffer; } function createPacketDecoderStream(maxPayload, binaryType) { if (!TEXT_DECODER) { TEXT_DECODER = new TextDecoder(); } const chunks = []; let state = 0 /* State.READ_HEADER */; let expectedLength = -1; let isBinary = false; return new TransformStream({ transform(chunk, controller) { chunks.push(chunk); while (true) { if (state === 0 /* State.READ_HEADER */) { if (totalLength(chunks) < 1) { break; } const header = concatChunks(chunks, 1); isBinary = (header[0] & 0x80) === 0x80; expectedLength = header[0] & 0x7f; if (expectedLength < 126) { state = 3 /* State.READ_PAYLOAD */; } else if (expectedLength === 126) { state = 1 /* State.READ_EXTENDED_LENGTH_16 */; } else { state = 2 /* State.READ_EXTENDED_LENGTH_64 */; } } else if (state === 1 /* State.READ_EXTENDED_LENGTH_16 */) { if (totalLength(chunks) < 2) { break; } const headerArray = concatChunks(chunks, 2); expectedLength = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length).getUint16(0); state = 3 /* State.READ_PAYLOAD */; } else if (state === 2 /* State.READ_EXTENDED_LENGTH_64 */) { if (totalLength(chunks) < 8) { break; } const headerArray = concatChunks(chunks, 8); const view = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length); const n = view.getUint32(0); if (n > Math.pow(2, 53 - 32) - 1) { // the maximum safe integer in JavaScript is 2^53 - 1 controller.enqueue(ERROR_PACKET); break; } expectedLength = n * Math.pow(2, 32) + view.getUint32(4); state = 3 /* State.READ_PAYLOAD */; } else { if (totalLength(chunks) < expectedLength) { break; } const data = concatChunks(chunks, expectedLength); controller.enqueue(decodePacket(isBinary ? data : TEXT_DECODER.decode(data), binaryType)); state = 0 /* State.READ_HEADER */; } if (expectedLength === 0 || expectedLength > maxPayload) { controller.enqueue(ERROR_PACKET); break; } } }, }); } const protocol$1 = 4; /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); } /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } // Remove event specific arrays for event types that no // one is subscribed for to avoid memory leak. if (callbacks.length === 0) { delete this._callbacks['$' + event]; } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = new Array(arguments.length - 1) , callbacks = this._callbacks['$' + event]; for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; // alias used for reserved events (protected method) Emitter.prototype.emitReserved = Emitter.prototype.emit; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; const globalThisShim = (() => { if (typeof self !== "undefined") { return self; } else if (typeof window !== "undefined") { return window; } else { return Function("return this")(); } })(); function pick(obj, ...attr) { return attr.reduce((acc, k) => { if (obj.hasOwnProperty(k)) { acc[k] = obj[k]; } return acc; }, {}); } // Keep a reference to the real timeout functions so they can be used when overridden const NATIVE_SET_TIMEOUT = globalThisShim.setTimeout; const NATIVE_CLEAR_TIMEOUT = globalThisShim.clearTimeout; function installTimerFunctions(obj, opts) { if (opts.useNativeTimers) { obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThisShim); obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThisShim); } else { obj.setTimeoutFn = globalThisShim.setTimeout.bind(globalThisShim); obj.clearTimeoutFn = globalThisShim.clearTimeout.bind(globalThisShim); } } // base64 encoded buffers are about 33% bigger (https://en.wikipedia.org/wiki/Base64) const BASE64_OVERHEAD = 1.33; // we could also have used `new Blob([obj]).size`, but it isn't supported in IE9 function byteLength(obj) { if (typeof obj === "string") { return utf8Length(obj); } // arraybuffer or blob return Math.ceil((obj.byteLength || obj.size) * BASE64_OVERHEAD); } function utf8Length(str) { let c = 0, length = 0; for (let i = 0, l = str.length; i < l; i++) { c = str.charCodeAt(i); if (c < 0x80) { length += 1; } else if (c < 0x800) { length += 2; } else if (c < 0xd800 || c >= 0xe000) { length += 3; } else { i++; length += 4; } } return length; } // imported from https://github.com/galkn/querystring /** * Compiles a querystring * Returns string representation of the object * * @param {Object} * @api private */ function encode$1(obj) { let str = ''; for (let i in obj) { if (obj.hasOwnProperty(i)) { if (str.length) str += '&'; str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); } } return str; } /** * Parses a simple querystring into an object * * @param {String} qs * @api private */ function decode(qs) { let qry = {}; let pairs = qs.split('&'); for (let i = 0, l = pairs.length; i < l; i++) { let pair = pairs[i].split('='); qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); } return qry; } class TransportError extends Error { constructor(reason, description, context) { super(reason); this.description = description; this.context = context; this.type = "TransportError"; } } class Transport extends Emitter { /** * Transport abstract constructor. * * @param {Object} opts - options * @protected */ constructor(opts) { super(); this.writable = false; installTimerFunctions(this, opts); this.opts = opts; this.query = opts.query; this.socket = opts.socket; } /** * Emits an error. * * @param {String} reason * @param description * @param context - the error context * @return {Transport} for chaining * @protected */ onError(reason, description, context) { super.emitReserved("error", new TransportError(reason, description, context)); return this; } /** * Opens the transport. */ open() { this.readyState = "opening"; this.doOpen(); return this; } /** * Closes the transport. */ close() { if (this.readyState === "opening" || this.readyState === "open") { this.doClose(); this.onClose(); } return this; } /** * Sends multiple packets. * * @param {Array} packets */ send(packets) { if (this.readyState === "open") { this.write(packets); } } /** * Called upon open * * @protected */ onOpen() { this.readyState = "open"; this.writable = true; super.emitReserved("open"); } /** * Called with data. * * @param {String} data * @protected */ onData(data) { const packet = decodePacket(data, this.socket.binaryType); this.onPacket(packet); } /** * Called with a decoded packet. * * @protected */ onPacket(packet) { super.emitReserved("packet", packet); } /** * Called upon close. * * @protected */ onClose(details) { this.readyState = "closed"; super.emitReserved("close", details); } /** * Pauses the transport, in order not to lose packets during an upgrade. * * @param onPause */ pause(onPause) { } createUri(schema, query = {}) { return (schema + "://" + this._hostname() + this._port() + this.opts.path + this._query(query)); } _hostname() { const hostname = this.opts.hostname; return hostname.indexOf(":") === -1 ? hostname : "[" + hostname + "]"; } _port() { if (this.opts.port && ((this.opts.secure && Number(this.opts.port !== 443)) || (!this.opts.secure && Number(this.opts.port) !== 80))) { return ":" + this.opts.port; } else { return ""; } } _query(query) { const encodedQuery = encode$1(query); return encodedQuery.length ? "?" + encodedQuery : ""; } } // imported from https://github.com/unshiftio/yeast const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64; let seed = 0, prev; /** * Return a string representing the specified number. * * @param {Number} num The number to convert. * @returns {String} The string representation of the number. * @api public */ function encode(num) { let encoded = ''; do { encoded = alphabet[num % length] + encoded; num = Math.floor(num / length); } while (num > 0); return encoded; } /** * Yeast: A tiny growing id generator. * * @returns {String} A unique id. * @api public */ function yeast() { const now = encode(+new Date()); if (now !== prev) return seed = 0, prev = now; return now + '.' + encode(seed++); } // imported from https://github.com/component/has-cors let value = false; try { value = typeof XMLHttpRequest !== 'undefined' && 'withCredentials' in new XMLHttpRequest(); } catch (err) { // if XMLHttp support is disabled in IE then it will throw // when trying to create } const hasCORS = value; // browser shim for xmlhttprequest module function XHR(opts) { const xdomain = opts.xdomain; // XMLHttpRequest can be disabled on IE try { if ("undefined" !== typeof XMLHttpRequest && (!xdomain || hasCORS)) { return new XMLHttpRequest(); } } catch (e) { } if (!xdomain) { try { return new globalThisShim[["Active"].concat("Object").join("X")]("Microsoft.XMLHTTP"); } catch (e) { } } } function createCookieJar() { } function empty() { } const hasXHR2 = (function () { const xhr = new XHR({ xdomain: false, }); return null != xhr.responseType; })(); class Polling extends Transport { /** * XHR Polling constructor. * * @param {Object} opts * @package */ constructor(opts) { super(opts); this.polling = false; if (typeof location !== "undefined") { const isSSL = "https:" === location.protocol; let port = location.port; // some user agents have empty `location.port` if (!port) { port = isSSL ? "443" : "80"; } this.xd = (typeof location !== "undefined" && opts.hostname !== location.hostname) || port !== opts.port; } /** * XHR supports binary */ const forceBase64 = opts && opts.forceBase64; this.supportsBinary = hasXHR2 && !forceBase64; if (this.opts.withCredentials) { this.cookieJar = createCookieJar(); } } get name() { return "polling"; } /** * Opens the socket (triggers polling). We write a PING message to determine * when the transport is open. * * @protected */ doOpen() { this.poll(); } /** * Pauses polling. * * @param {Function} onPause - callback upon buffers are flushed and transport is paused * @package */ pause(onPause) { this.readyState = "pausing"; const pause = () => { this.readyState = "paused"; onPause(); }; if (this.polling || !this.writable) { let total = 0; if (this.polling) { total++; this.once("pollComplete", function () { --total || pause(); }); } if (!this.writable) { total++; this.once("drain", function () { --total || pause(); }); } } else { pause(); } } /** * Starts polling cycle. * * @private */ poll() { this.polling = true; this.doPoll(); this.emitReserved("poll"); } /** * Overloads onData to detect payloads. * * @protected */ onData(data) { const callback = (packet) => { // if its the first message we consider the transport open if ("opening" === this.readyState && packet.type === "open") { this.onOpen(); } // if its a close packet, we close the ongoing requests if ("close" === packet.type) { this.onClose({ description: "transport closed by the server" }); return false; } // otherwise bypass onData and handle the message this.onPacket(packet); }; // decode payload decodePayload(data, this.socket.binaryType).forEach(callback); // if an event did not trigger closing if ("closed" !== this.readyState) { // if we got data we're not polling this.polling = false; this.emitReserved("pollComplete"); if ("open" === this.readyState) { this.poll(); } } } /** * For polling, send a close packet. * * @protected */ doClose() { const close = () => { this.write([{ type: "close" }]); }; if ("open" === this.readyState) { close(); } else { // in case we're trying to close while // handshaking is in progress (GH-164) this.once("open", close); } } /** * Writes a packets payload. * * @param {Array} packets - data packets * @protected */ write(packets) { this.writable = false; encodePayload(packets, (data) => { this.doWrite(data, () => { this.writable = true; this.emitReserved("drain"); }); }); } /** * Generates uri for connection. * * @private */ uri() { const schema = this.opts.secure ? "https" : "http"; const query = this.query || {}; // cache busting is forced if (false !== this.opts.timestampRequests) { query[this.opts.timestampParam] = yeast(); } if (!this.supportsBinary && !query.sid) { query.b64 = 1; } return this.createUri(schema, query); } /** * Creates a request. * * @param {String} method * @private */ request(opts = {}) { Object.assign(opts, { xd: this.xd, cookieJar: this.cookieJar }, this.opts); return new Request$1(this.uri(), opts); } /** * Sends data. * * @param {String} data to send. * @param {Function} called upon flush. * @private */ doWrite(data, fn) { const req = this.request({ method: "POST", data: data, }); req.on("success", fn); req.on("error", (xhrStatus, context) => { this.onError("xhr post error", xhrStatus, context); }); } /** * Starts a poll cycle. * * @private */ doPoll() { const req = this.request(); req.on("data", this.onData.bind(this)); req.on("error", (xhrStatus, context) => { this.onError("xhr poll error", xhrStatus, context); }); this.pollXhr = req; } } class Request$1 extends Emitter { /** * Request constructor * * @param {Object} options * @package */ constructor(uri, opts) { super(); installTimerFunctions(this, opts); this.opts = opts; this.method = opts.method || "GET"; this.uri = uri; this.data = undefined !== opts.data ? opts.data : null; this.create(); } /** * Creates the XHR object and sends the request. * * @private */ create() { var _a; const opts = pick(this.opts, "agent", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "autoUnref"); opts.xdomain = !!this.opts.xd; const xhr = (this.xhr = new XHR(opts)); try { xhr.open(this.method, this.uri, true); try { if (this.opts.extraHeaders) { xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true); for (let i in this.opts.extraHeaders) { if (this.opts.extraHeaders.hasOwnProperty(i)) { xhr.setRequestHeader(i, this.opts.extraHeaders[i]); } } } } catch (e) { } if ("POST" === this.method) { try { xhr.setRequestHeader("Content-type", "text/plain;charset=UTF-8"); } catch (e) { } } try { xhr.setRequestHeader("Accept", "*/*"); } catch (e) { } (_a = this.opts.cookieJar) === null || _a === void 0 ? void 0 : _a.addCookies(xhr); // ie6 check if ("withCredentials" in xhr) { xhr.withCredentials = this.opts.withCredentials; } if (this.opts.requestTimeout) { xhr.timeout = this.opts.requestTimeout; } xhr.onreadystatechange = () => { var _a; if (xhr.readyState === 3) { (_a = this.opts.cookieJar) === null || _a === void 0 ? void 0 : _a.parseCookies(xhr); } if (4 !== xhr.readyState) return; if (200 === xhr.status || 1223 === xhr.status) { this.onLoad(); } else { // make sure the `error` event handler that's user-set // does not throw in the same tick and gets caught here this.setTimeoutFn(() => { this.onError(typeof xhr.status === "number" ? xhr.status : 0); }, 0); } }; xhr.send(this.data); } catch (e) { // Need to defer since .create() is called directly from the constructor // and thus the 'error' event can only be only bound *after* this exception // occurs. Therefore, also, we cannot throw here at all. this.setTimeoutFn(() => { this.onError(e); }, 0); return; } if (typeof document !== "undefined") { this.index = Request$1.requestsCount++; Request$1.requests[this.index] = this; } } /** * Called upon error. * * @private */ onError(err) { this.emitReserved("error", err, this.xhr); this.cleanup(true); } /** * Cleans up house. * * @private */ cleanup(fromError) { if ("undefined" === typeof this.xhr || null === this.xhr) { return; } this.xhr.onreadystatechange = empty; if (fromError) { try { this.xhr.abort(); } catch (e) { } } if (typeof document !== "und